[ Index ]

PHP Cross Reference of vtigercrm-6.1.0

title

Body

[close]

/libraries/tcpdf/ -> tcpdf.php (source)

   1  <?php
   2  //============================================================+

   3  // File name   : tcpdf.php

   4  // Begin       : 2002-08-03

   5  // Last Update : 2009-05-23

   6  // Author      : Nicola Asuni - [email protected] - http://www.tcpdf.org

   7  // Version     : 4.6.012

   8  // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)

   9  //     ----------------------------------------------------------------------------

  10  //  Copyright (C) 2002-2009  Nicola Asuni - Tecnick.com S.r.l.

  11  //     

  12  //     This program is free software: you can redistribute it and/or modify

  13  //     it under the terms of the GNU Lesser General Public License as published by

  14  //     the Free Software Foundation, either version 2.1 of the License, or

  15  //     (at your option) any later version.

  16  //     

  17  //     This program is distributed in the hope that it will be useful,

  18  //     but WITHOUT ANY WARRANTY; without even the implied warranty of

  19  //     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

  20  //     GNU Lesser General Public License for more details.

  21  //     

  22  //     You should have received a copy of the GNU Lesser General Public License

  23  //     along with this program.  If not, see <http://www.gnu.org/licenses/>.

  24  //     

  25  //     See LICENSE.TXT file for more information.

  26  //  ----------------------------------------------------------------------------

  27  //

  28  // Description : This is a PHP class for generating PDF documents without 

  29  //               requiring external extensions.

  30  //

  31  // NOTE:

  32  // This class was originally derived in 2002 from the Public 

  33  // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 

  34  // but now is almost entirely rewritten.

  35  //

  36  // Main features:

  37  //  * no external libraries are required for the basic functions;

  38  //     * supports all ISO page formats;

  39  //     * supports custom page formats, margins and units of measure;

  40  //     * supports UTF-8 Unicode and Right-To-Left languages;

  41  //     * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;

  42  //     * supports document encryption;

  43  //     * includes methods to publish some XHTML code;

  44  //     * includes graphic (geometric) and transformation methods;

  45  //     * includes Javascript and forms support;

  46  //     * includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;

  47  //     * includes methods to set Bookmarks and print a Table of Content;

  48  //     * includes methods to move and delete pages;

  49  //     * includes methods for automatic page header and footer management;

  50  //     * supports automatic page break;

  51  //     * supports automatic page numbering and page groups;

  52  //     * supports automatic line break and text justification;

  53  //     * supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)

  54  //     * supports stroke and clipping mode for text;

  55  //     * supports clipping masks;

  56  //     * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;

  57  //     * supports several annotations, including links, text and file attachments;

  58  //     * supports page compression (requires zlib extension);

  59  //  * supports text hyphenation.

  60  //  * supports transactions to UNDO commands.

  61  //

  62  // -----------------------------------------------------------

  63  // THANKS TO:

  64  // 

  65  // Olivier Plathey (http://www.fpdf.org) for original FPDF.

  66  // Efthimios Mavrogeorgiadis ([email protected]) for suggestions on RTL language support.

  67  // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.

  68  // Warren Sherliker ([email protected]) for better image handling.

  69  // dullus for text Justification.

  70  // Bob Vincent ([email protected]) for <li> value attribute.

  71  // Patrick Benny for text stretch suggestion on Cell().

  72  // Johannes G�ntert for JavaScript support.

  73  // Denis Van Nuffelen for Dynamic Form.

  74  // Jacek Czekaj for multibyte justification

  75  // Anthony Ferrara for the reintroduction of legacy image methods.

  76  // Sourceforge user 1707880 (hucste) for line-trough mode.

  77  // Larry Stanbery for page groups.

  78  // Martin Hall-May for transparency.

  79  // Aaron C. Spike for Polycurve method.

  80  // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.

  81  // Moritz Wagner and Andreas Wurmser for graphic functions.

  82  // Andrew Whitehead for core fonts support.

  83  // Esteban Jo�l Mar�n for OpenType font conversion.

  84  // Teus Hagen for several suggestions and fixes.

  85  // Yukihiro Nakadaira for CID-0 CJK fonts fixes.

  86  // Kosmas Papachristos for some CSS improvements.

  87  // Marcel Partap for some fixes.

  88  // Won Kyu Park for several suggestions, fixes and patches.

  89  // Anyone that has reported a bug or sent a suggestion.

  90  //============================================================+

  91  
  92  /**

  93   * This is a PHP class for generating PDF documents without requiring external extensions.<br>

  94   * 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>

  95   * <h3>TCPDF main features are:</h3>

  96   * <ul>

  97  * <li>no external libraries are required for the basic functions;</li>

  98  * <li>supports all ISO page formats;</li>

  99  * <li>supports custom page formats, margins and units of measure;</li>

 100  * <li>supports UTF-8 Unicode and Right-To-Left languages;</li>

 101  * <li>supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li>

 102  * <li>supports document encryption;</li>

 103  * <li>includes methods to publish some XHTML code;</li>

 104  * <li>includes graphic (geometric) and transformation methods;</li>

 105  * <li>includes Javascript and forms support;</li>

 106  * <li>includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;</li>

 107  * <li>includes methods to set Bookmarks and print a Table of Content;</li>

 108  * <li>includes methods to move and delete pages;</li>

 109  * <li>includes methods for automatic page header and footer management;</li>

 110  * <li>supports automatic page break;</li>

 111  * <li>supports automatic page numbering and page groups;</li>

 112  * <li>supports automatic line break and text justification;</li>

 113  * <li>supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)</li>

 114  * <li>supports stroke and clipping mode for text;</li>

 115  * <li>supports clipping masks;</li>

 116  * <li>supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>

 117  * <li>supports several annotations, including links, text and file attachments;</li>

 118  * <li>supports page compression (requires zlib extension);</li>

 119  * <li>supports text hyphenation.</li>

 120  * <li>supports transactions to UNDO commands.</li>

 121   * </ul>

 122   * Tools to encode your unicode fonts are on fonts/utils directory.</p>

 123   * @package com.tecnick.tcpdf

 124   * @abstract Class for generating PDF files on-the-fly without requiring external extensions.

 125   * @author Nicola Asuni

 126   * @copyright 2002-2009 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - [email protected]

 127   * @link http://www.tcpdf.org

 128   * @license http://www.gnu.org/copyleft/lesser.html LGPL

 129   * @version 4.6.012

 130   */
 131  
 132  /**

 133   * main configuration file

 134   */
 135  require_once(dirname(__FILE__).'/config/tcpdf_config.php');
 136  
 137  // includes some support files

 138  
 139  /**

 140   * unicode data

 141   */
 142  require_once(dirname(__FILE__).'/unicode_data.php');
 143  
 144  /**

 145   * html colors table

 146   */
 147  require_once(dirname(__FILE__).'/htmlcolors.php');
 148  
 149  if (!class_exists('TCPDF', false)) {
 150      /**

 151       * define default PDF document producer

 152       */ 
 153      define('PDF_PRODUCER', 'TCPDF 4.6.012 (http://www.tcpdf.org)');
 154      
 155      /**

 156      * This is a PHP class for generating PDF documents without requiring external extensions.<br>

 157      * 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>

 158      * @name TCPDF

 159      * @package com.tecnick.tcpdf

 160      * @version 4.6.012

 161      * @author Nicola Asuni - [email protected]

 162      * @link http://www.tcpdf.org

 163      * @license http://www.gnu.org/copyleft/lesser.html LGPL

 164      */
 165      class TCPDF {
 166          
 167          // protected or Protected properties

 168  
 169          /**

 170          * @var current page number

 171          * @access protected

 172          */
 173          protected $page;
 174          
 175          /**

 176          * @var current object number

 177          * @access protected

 178          */
 179          protected $n;
 180  
 181          /**

 182          * @var array of object offsets

 183          * @access protected

 184          */
 185          protected $offsets;
 186  
 187          /**

 188          * @var buffer holding in-memory PDF

 189          * @access protected

 190          */
 191          protected $buffer;
 192  
 193          /**

 194          * @var array containing pages

 195          * @access protected

 196          */
 197          protected $pages = array();
 198  
 199          /**

 200          * @var current document state

 201          * @access protected

 202          */
 203          protected $state;
 204  
 205          /**

 206          * @var compression flag

 207          * @access protected

 208          */
 209          protected $compress;
 210          
 211          /**

 212          * @var current page orientation (P = Portrait, L = Landscape)

 213          * @access protected

 214          */
 215          protected $CurOrientation;
 216  
 217          /**

 218          * @var array that stores page dimensions and graphic status.<ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul>

 219          * @access protected

 220          */
 221          protected $pagedim = array();
 222  
 223          /**

 224          * @var scale factor (number of points in user unit)

 225          * @access protected

 226          */
 227          protected $k;
 228  
 229          /**

 230          * @var width of page format in points

 231          * @access protected

 232          */
 233          protected $fwPt;
 234  
 235          /**

 236          * @var height of page format in points

 237          * @access protected

 238          */
 239          protected $fhPt;
 240  
 241          /**

 242          * @var current width of page in points

 243          * @access protected

 244          */
 245          protected $wPt;
 246  
 247          /**

 248          * @var current height of page in points

 249          * @access protected

 250          */
 251          protected $hPt;
 252  
 253          /**

 254          * @var current width of page in user unit

 255          * @access protected

 256          */
 257          protected $w;
 258  
 259          /**

 260          * @var current height of page in user unit

 261          * @access protected

 262          */
 263          protected $h;
 264  
 265          /**

 266          * @var left margin

 267          * @access protected

 268          */
 269          protected $lMargin;
 270  
 271          /**

 272          * @var top margin

 273          * @access protected

 274          */
 275          protected $tMargin;
 276  
 277          /**

 278          * @var right margin

 279          * @access protected

 280          */
 281          protected $rMargin;
 282  
 283          /**

 284          * @var page break margin

 285          * @access protected

 286          */
 287          protected $bMargin;
 288  
 289          /**

 290          * @var cell internal padding

 291          * @access protected

 292          */
 293          //protected

 294          public $cMargin;
 295          
 296          /**

 297          * @var cell internal padding (previous value)

 298          * @access protected

 299          */
 300          protected $oldcMargin;
 301  
 302          /**

 303          * @var current horizontal position in user unit for cell positioning

 304          * @access protected

 305          */
 306          protected $x;
 307  
 308          /**

 309          * @var current vertical position in user unit for cell positioning

 310          * @access protected

 311          */
 312          protected $y;
 313  
 314          /**

 315          * @var height of last cell printed

 316          * @access protected

 317          */
 318          protected $lasth;
 319  
 320          /**

 321          * @var line width in user unit

 322          * @access protected

 323          */
 324          protected $LineWidth;
 325  
 326          /**

 327          * @var array of standard font names

 328          * @access protected

 329          */
 330          protected $CoreFonts;
 331  
 332          /**

 333          * @var array of used fonts

 334          * @access protected

 335          */
 336          protected $fonts = array();
 337  
 338          /**

 339          * @var array of font files

 340          * @access protected

 341          */
 342          protected $FontFiles = array();
 343  
 344          /**

 345          * @var array of encoding differences

 346          * @access protected

 347          */
 348          protected $diffs = array();
 349  
 350          /**

 351          * @var array of used images

 352          * @access protected

 353          */
 354          protected $images = array();
 355  
 356          /**

 357          * @var array of Annotations in pages

 358          * @access protected

 359          */
 360          protected $PageAnnots = array();
 361  
 362          /**

 363          * @var array of internal links

 364          * @access protected

 365          */
 366          protected $links = array();
 367  
 368          /**

 369          * @var current font family

 370          * @access protected

 371          */
 372          protected $FontFamily;
 373  
 374          /**

 375          * @var current font style

 376          * @access protected

 377          */
 378          protected $FontStyle;
 379          
 380          /**

 381          * @var current font ascent (distance between font top and baseline)

 382          * @access protected

 383          * @since 2.8.000 (2007-03-29)

 384          */
 385          protected $FontAscent;
 386          
 387          /**

 388          * @var current font descent (distance between font bottom and baseline)

 389          * @access protected

 390          * @since 2.8.000 (2007-03-29)

 391          */
 392          protected $FontDescent;
 393  
 394          /**

 395          * @var underlining flag

 396          * @access protected

 397          */
 398          protected $underline;
 399  
 400          /**

 401          * @var current font info

 402          * @access protected

 403          */
 404          protected $CurrentFont;
 405  
 406          /**

 407          * @var current font size in points

 408          * @access protected

 409          */
 410          protected $FontSizePt;
 411  
 412          /**

 413          * @var current font size in user unit

 414          * @access protected

 415          */
 416          protected $FontSize;
 417  
 418          /**

 419          * @var commands for drawing color

 420          * @access protected

 421          */
 422          protected $DrawColor;
 423  
 424          /**

 425          * @var commands for filling color

 426          * @access protected

 427          */
 428          protected $FillColor;
 429  
 430          /**

 431          * @var commands for text color

 432          * @access protected

 433          */
 434          protected $TextColor;
 435  
 436          /**

 437          * @var indicates whether fill and text colors are different

 438          * @access protected

 439          */
 440          protected $ColorFlag;
 441  
 442          /**

 443          * @var automatic page breaking

 444          * @access protected

 445          */
 446          protected $AutoPageBreak;
 447  
 448          /**

 449          * @var threshold used to trigger page breaks

 450          * @access protected

 451          */
 452          protected $PageBreakTrigger;
 453  
 454          /**

 455          * @var flag set when processing footer

 456          * @access protected

 457          */
 458          protected $InFooter = false;
 459  
 460          /**

 461          * @var zoom display mode

 462          * @access protected

 463          */
 464          protected $ZoomMode;
 465  
 466          /**

 467          * @var layout display mode

 468          * @access protected

 469          */
 470          protected $LayoutMode;
 471  
 472          /**

 473          * @var title

 474          * @access protected

 475          */
 476          protected $title = '';
 477  
 478          /**

 479          * @var subject

 480          * @access protected

 481          */
 482          protected $subject = '';
 483  
 484          /**

 485          * @var author

 486          * @access protected

 487          */
 488          protected $author = '';
 489  
 490          /**

 491          * @var keywords

 492          * @access protected

 493          */
 494          protected $keywords = '';
 495  
 496          /**

 497          * @var creator

 498          * @access protected

 499          */
 500          protected $creator = '';
 501  
 502          /**

 503          * @var alias for total number of pages

 504          * @access protected

 505          */
 506          protected $AliasNbPages = '{nb}';
 507          
 508          /**

 509          * @var alias for page number

 510          * @access protected

 511          */
 512          protected $AliasNumPage = '{pnb}';
 513          
 514          /**

 515          * @var right-bottom corner X coordinate of inserted image

 516          * @since 2002-07-31

 517          * @author Nicola Asuni

 518          * @access protected

 519          */
 520          protected $img_rb_x;
 521  
 522          /**

 523          * @var right-bottom corner Y coordinate of inserted image

 524          * @since 2002-07-31

 525          * @author Nicola Asuni

 526          * @access protected

 527          */
 528          protected $img_rb_y;
 529  
 530          /**

 531          * @var adjusting factor to convert pixels to user units.

 532          * @since 2004-06-14

 533          * @author Nicola Asuni

 534          * @access protected

 535          */
 536          protected $imgscale = 1;
 537  
 538          /**

 539          * @var boolean set to true when the input text is unicode (require unicode fonts)

 540          * @since 2005-01-02

 541          * @author Nicola Asuni

 542          * @access protected

 543          */
 544          protected $isunicode = false;
 545  
 546          /**

 547          * @var PDF version

 548          * @since 1.5.3

 549          * @access protected

 550          */
 551          protected $PDFVersion = '1.7';
 552          
 553          
 554          // ----------------------

 555          
 556          /**

 557           * @var Minimum distance between header and top page margin.

 558           * @access protected

 559           */
 560          protected $header_margin;
 561          
 562          /**

 563           * @var Minimum distance between footer and bottom page margin.

 564           * @access protected

 565           */
 566          protected $footer_margin;
 567          
 568          /**

 569           * @var original left margin value

 570           * @access protected

 571           * @since 1.53.0.TC013

 572           */
 573          protected $original_lMargin;
 574          
 575          /**

 576           * @var original right margin value

 577           * @access protected

 578           * @since 1.53.0.TC013

 579           */
 580          protected $original_rMargin;
 581              
 582          /**

 583           * @var Header font.

 584           * @access protected

 585           */
 586          protected $header_font;
 587          
 588          /**

 589           * @var Footer font.

 590           * @access protected

 591           */
 592          protected $footer_font;
 593          
 594          /**

 595           * @var Language templates.

 596           * @access protected

 597           */
 598          protected $l;
 599          
 600          /**

 601           * @var Barcode to print on page footer (only if set).

 602           * @access protected

 603           */
 604          protected $barcode = false;
 605          
 606          /**

 607           * @var If true prints header

 608           * @access protected

 609           */
 610          protected $print_header = true;
 611          
 612          /**

 613           * @var If true prints footer.

 614           * @access protected

 615           */
 616          protected $print_footer = true;
 617              
 618          /**

 619           * @var Header image logo.

 620           * @access protected

 621           */
 622          protected $header_logo = '';
 623          
 624          /**

 625           * @var Header image logo width in mm.

 626           * @access protected

 627           */
 628          protected $header_logo_width = 30;
 629          
 630          /**

 631           * @var String to print as title on document header.

 632           * @access protected

 633           */
 634          protected $header_title = '';
 635          
 636          /**

 637           * @var String to print on document header.

 638           * @access protected

 639           */
 640          protected $header_string = '';
 641          
 642          /**

 643           * @var Default number of columns for html table.

 644           * @access protected

 645           */
 646          protected $default_table_columns = 4;
 647          
 648          
 649          // variables for html parser

 650          
 651          /**

 652           * @var HTML PARSER: array to store current link and rendering styles.

 653           * @access protected

 654           */
 655          protected $HREF = array();
 656          
 657          /**

 658           * @var store a list of available fonts on filesystem.

 659           * @access protected

 660           */
 661          protected $fontlist = array();
 662          
 663          /**

 664           * @var current foreground color

 665           * @access protected

 666           */
 667          protected $fgcolor;
 668                          
 669          /**

 670           * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.

 671           * @access protected

 672           */
 673          protected $listordered = array();
 674          
 675          /**

 676           * @var HTML PARSER: array count list items on nested lists.

 677           * @access protected

 678           */
 679          protected $listcount = array();
 680          
 681          /**

 682           * @var HTML PARSER: current list nesting level.

 683           * @access protected

 684           */
 685          protected $listnum = 0;
 686          
 687          /**

 688           * @var HTML PARSER: indent amount for lists.

 689           * @access protected

 690           */
 691          protected $listindent;
 692          
 693          /**

 694           * @var current background color

 695           * @access protected

 696           */
 697          protected $bgcolor;
 698          
 699          /**

 700           * @var Store temporary font size in points.

 701           * @access protected

 702           */
 703          protected $tempfontsize = 10;
 704          
 705          /**

 706           * @var spacer for LI tags.

 707           * @access protected

 708           */
 709          protected $lispacer = '';
 710          
 711          /**

 712           * @var default encoding

 713           * @access protected

 714           * @since 1.53.0.TC010

 715           */
 716          protected $encoding = 'UTF-8';
 717          
 718          /**

 719           * @var PHP internal encoding

 720           * @access protected

 721           * @since 1.53.0.TC016

 722           */
 723          protected $internal_encoding;
 724          
 725          /**

 726           * @var indicates if the document language is Right-To-Left

 727           * @access protected

 728           * @since 2.0.000

 729           */
 730          protected $rtl = false;
 731          
 732          /**

 733           * @var used to force RTL or LTR string inversion

 734           * @access protected

 735           * @since 2.0.000

 736           */
 737          protected $tmprtl = false;
 738          
 739          // --- Variables used for document encryption:

 740          
 741          /**

 742           * Indicates whether document is protected

 743           * @access protected

 744           * @since 2.0.000 (2008-01-02)

 745           */
 746          protected $encrypted;
 747          
 748          /**

 749           * U entry in pdf document

 750           * @access protected

 751           * @since 2.0.000 (2008-01-02)

 752           */
 753          protected $Uvalue;
 754          
 755          /**

 756           * O entry in pdf document

 757           * @access protected

 758           * @since 2.0.000 (2008-01-02)

 759           */
 760          protected $Ovalue;
 761          
 762          /**

 763           * P entry in pdf document

 764           * @access protected

 765           * @since 2.0.000 (2008-01-02)

 766           */
 767          protected $Pvalue;
 768          
 769          /**

 770           * encryption object id

 771           * @access protected

 772           * @since 2.0.000 (2008-01-02)

 773           */
 774          protected $enc_obj_id;
 775          
 776          /**

 777           * last RC4 key encrypted (cached for optimisation)

 778           * @access protected

 779           * @since 2.0.000 (2008-01-02)

 780           */
 781          protected $last_rc4_key;
 782          
 783          /**

 784           * last RC4 computed key

 785           * @access protected

 786           * @since 2.0.000 (2008-01-02)

 787           */
 788          protected $last_rc4_key_c;
 789          
 790          /**

 791           * RC4 padding

 792           * @access protected

 793           */
 794          protected $padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
 795          
 796          /**

 797           * RC4 encryption key

 798           * @access protected

 799           */
 800          protected $encryption_key;
 801          
 802          // --- bookmark ---

 803          
 804          /**

 805           * Outlines for bookmark

 806           * @access protected

 807           * @since 2.1.002 (2008-02-12)

 808           */
 809          protected $outlines = array();
 810          
 811          /**

 812           * Outline root for bookmark

 813           * @access protected

 814           * @since 2.1.002 (2008-02-12)

 815           */
 816          protected $OutlineRoot;
 817          
 818          
 819          // --- javascript and form ---

 820          
 821          /**

 822           * javascript code

 823           * @access protected

 824           * @since 2.1.002 (2008-02-12)

 825           */
 826          protected $javascript = '';
 827          
 828          /**

 829           * javascript counter

 830           * @access protected

 831           * @since 2.1.002 (2008-02-12)

 832           */
 833          protected $n_js;
 834  
 835          /**

 836           * line trough state

 837           * @access protected

 838           * @since 2.8.000 (2008-03-19)

 839           */
 840          protected $linethrough;
 841  
 842          // --- Variables used for User's Rights ---

 843          // See PDF reference chapter 8.7 Digital Signatures

 844  
 845          /**

 846           * If true enables user's rights on PDF reader

 847           * @access protected

 848           * @since 2.9.000 (2008-03-26)

 849           */
 850          protected $ur;
 851  
 852          /**

 853           * Names specifying additional document-wide usage rights for the document.

 854           * @access protected

 855           * @since 2.9.000 (2008-03-26)

 856           */
 857          protected $ur_document;
 858  
 859          /**

 860           * Names specifying additional annotation-related usage rights for the document.

 861           * @access protected

 862           * @since 2.9.000 (2008-03-26)

 863           */
 864          protected $ur_annots;
 865  
 866          /**

 867           * Names specifying additional form-field-related usage rights for the document.

 868           * @access protected

 869           * @since 2.9.000 (2008-03-26)

 870           */
 871          protected $ur_form;
 872  
 873          /**

 874           * Names specifying additional signature-related usage rights for the document.

 875           * @access protected

 876           * @since 2.9.000 (2008-03-26)

 877           */
 878          protected $ur_signature;
 879  
 880          /**

 881           * Dot Per Inch Document Resolution (do not change)

 882           * @access protected

 883           * @since 3.0.000 (2008-03-27)

 884           */
 885          protected $dpi = 72;
 886          
 887          /**

 888           * Array of page numbers were a new page group was started

 889           * @access protected

 890           * @since 3.0.000 (2008-03-27)

 891           */
 892          protected $newpagegroup = array();
 893          
 894          /**

 895           * Contains the number of pages of the groups

 896           * @access protected

 897           * @since 3.0.000 (2008-03-27)

 898           */
 899          protected $pagegroups;
 900          
 901          /**

 902           * Contains the alias of the current page group

 903           * @access protected

 904           * @since 3.0.000 (2008-03-27)

 905           */
 906          protected $currpagegroup; 
 907          
 908          /**

 909           * Restrict the rendering of some elements to screen or printout.

 910           * @access protected

 911           * @since 3.0.000 (2008-03-27)

 912           */
 913          protected $visibility = 'all';
 914          
 915          /**

 916           * Print visibility.

 917           * @access protected

 918           * @since 3.0.000 (2008-03-27)

 919           */
 920          protected $n_ocg_print;
 921          
 922          /**

 923           * View visibility.

 924           * @access protected

 925           * @since 3.0.000 (2008-03-27)

 926           */
 927          protected $n_ocg_view;
 928          
 929          /**

 930           * Array of transparency objects and parameters.

 931           * @access protected

 932           * @since 3.0.000 (2008-03-27)

 933           */
 934          protected $extgstates;
 935          
 936          /**

 937           * Set the default JPEG compression quality (1-100)

 938           * @access protected

 939           * @since 3.0.000 (2008-03-27)

 940           */
 941          protected $jpeg_quality;
 942          
 943          /**

 944           * Default cell height ratio.

 945           * @access protected

 946           * @since 3.0.014 (2008-05-23)

 947           */
 948          protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
 949          
 950          /**

 951           * PDF viewer preferences.

 952           * @access protected

 953           * @since 3.1.000 (2008-06-09)

 954           */
 955          protected $viewer_preferences;
 956          
 957          /**

 958           * A name object specifying how the document should be displayed when opened.

 959           * @access protected

 960           * @since 3.1.000 (2008-06-09)

 961           */
 962          protected $PageMode;
 963          
 964          /**

 965           * Array for storing gradient information.

 966           * @access protected

 967           * @since 3.1.000 (2008-06-09)

 968           */
 969          protected $gradients = array();
 970          
 971          /**

 972           * Array used to store positions inside the pages buffer.

 973           * keys are the page numbers

 974           * @access protected

 975           * @since 3.2.000 (2008-06-26)

 976           */
 977          protected $intmrk = array();
 978          
 979          /**

 980           * Array used to store footer positions of each page.

 981           * @access protected

 982           * @since 3.2.000 (2008-07-01)

 983           */
 984          protected $footerpos = array();
 985          
 986          
 987          /**

 988           * Array used to store footer lenght of each page.

 989           * @access protected

 990           * @since 4.0.014 (2008-07-29)

 991           */
 992          protected $footerlen = array();
 993          
 994          /**

 995           * True if a newline is created.

 996           * @access protected

 997           * @since 3.2.000 (2008-07-01)

 998           */
 999          protected $newline = true;
1000          
1001          /**

1002           * End position of the latest inserted line

1003           * @access protected

1004           * @since 3.2.000 (2008-07-01)

1005           */
1006          protected $endlinex = 0;
1007          
1008          /**

1009           * PDF string for last line width

1010           * @access protected

1011           * @since 4.0.006 (2008-07-16)

1012           */
1013          protected $linestyleWidth = '';
1014          
1015          /**

1016           * PDF string for last line width

1017           * @access protected

1018           * @since 4.0.006 (2008-07-16)

1019           */
1020          protected $linestyleCap = '0 J';
1021          
1022          /**

1023           * PDF string for last line width

1024           * @access protected

1025           * @since 4.0.006 (2008-07-16)

1026           */
1027          protected $linestyleJoin = '0 j';
1028          
1029          /**

1030           * PDF string for last line width

1031           * @access protected

1032           * @since 4.0.006 (2008-07-16)

1033           */
1034          protected $linestyleDash = '[] 0 d';
1035          
1036          /**

1037           * True if marked-content sequence is open

1038           * @access protected

1039           * @since 4.0.013 (2008-07-28)

1040           */
1041          protected $openMarkedContent = false;
1042          
1043          /**

1044           * Count the latest inserted vertical spaces on HTML

1045           * @access protected

1046           * @since 4.0.021 (2008-08-24)

1047           */
1048          protected $htmlvspace = 0;
1049          
1050          /**

1051           * Array of Spot colors

1052           * @access protected

1053           * @since 4.0.024 (2008-09-12)

1054           */
1055          protected $spot_colors = array();
1056          
1057          /**

1058           * Symbol used for HTML unordered list items

1059           * @access protected

1060           * @since 4.0.028 (2008-09-26)

1061           */
1062          protected $lisymbol = '';
1063          
1064          /**

1065           * String used to mark the beginning and end of EPS image blocks

1066           * @access protected

1067           * @since 4.1.000 (2008-10-18)

1068           */
1069          protected $epsmarker = 'x#!#EPS#!#x';
1070          
1071          /**

1072           * Array of transformation matrix

1073           * @access protected

1074           * @since 4.2.000 (2008-10-29)

1075           */
1076          protected $transfmatrix = array();
1077          
1078          /**

1079           * Booklet mode for double-sided pages

1080           * @access protected

1081           * @since 4.2.000 (2008-10-29)

1082           */
1083          protected $booklet = false;
1084          
1085          /**

1086           * Epsilon value used for float calculations

1087           * @access protected

1088           * @since 4.2.000 (2008-10-29)

1089           */
1090          protected $feps = 0.001;
1091          
1092          /**

1093           * Array used for custom vertical spaces for HTML tags

1094           * @access protected

1095           * @since 4.2.001 (2008-10-30)

1096           */
1097          protected $tagvspaces = array();
1098          
1099          /**

1100           * @var HTML PARSER: custom indent amount for lists.

1101           * Negative value means disabled.

1102           * @access protected

1103           * @since 4.2.007 (2008-11-12)

1104           */
1105          protected $customlistindent = -1;
1106          
1107          /**

1108           * @var if true keeps the border open for the cell sides that cross the page.

1109           * @access protected

1110           * @since 4.2.010 (2008-11-14)

1111           */
1112          protected $opencell = true;
1113  
1114          /**

1115           * @var array of files to embedd

1116           * @access protected

1117           * @since 4.4.000 (2008-12-07)

1118           */
1119          protected $embeddedfiles = array();
1120  
1121          /**

1122           * @var boolean true when inside html pre tag

1123           * @access protected

1124           * @since 4.4.001 (2008-12-08)

1125           */
1126          protected $premode = false;
1127  
1128          /**

1129           * Array used to store positions of graphics transformation blocks inside the page buffer.

1130           * keys are the page numbers

1131           * @access protected

1132           * @since 4.4.002 (2008-12-09)

1133           */
1134          protected $transfmrk = array();
1135  
1136          /**

1137           * Default color for html links

1138           * @access protected

1139           * @since 4.4.003 (2008-12-09)

1140           */
1141          protected $htmlLinkColorArray = array(0, 0, 255);
1142  
1143          /**

1144           * Default font style to add to html links

1145           * @access protected

1146           * @since 4.4.003 (2008-12-09)

1147           */
1148          protected $htmlLinkFontStyle = 'U';
1149  
1150          /**

1151           * Counts the number of pages.

1152           * @access protected

1153           * @since 4.5.000 (2008-12-31)

1154           */
1155          protected $numpages = 0;
1156  
1157          /**

1158           * Array containing page lenghts in bytes.

1159           * @access protected

1160           * @since 4.5.000 (2008-12-31)

1161           */
1162          protected $pagelen = array();
1163  
1164          /**

1165           * Counts the number of pages.

1166           * @access protected

1167           * @since 4.5.000 (2008-12-31)

1168           */
1169          protected $numimages = 0;
1170  
1171          /**

1172           * Store the image keys.

1173           * @access protected

1174           * @since 4.5.000 (2008-12-31)

1175           */
1176          protected $imagekeys = array();
1177  
1178          /**

1179           * Lenght of the buffer in bytes.

1180           * @access protected

1181           * @since 4.5.000 (2008-12-31)

1182           */
1183          protected $bufferlen = 0;
1184  
1185          /**

1186           * If true enables disk caching.

1187           * @access protected

1188           * @since 4.5.000 (2008-12-31)

1189           */
1190          protected $diskcache = false;
1191  
1192          /**

1193           * Counts the number of fonts.

1194           * @access protected

1195           * @since 4.5.000 (2009-01-02)

1196           */
1197          protected $numfonts = 0;
1198  
1199          /**

1200           * Store the font keys.

1201           * @access protected

1202           * @since 4.5.000 (2009-01-02)

1203           */
1204          protected $fontkeys = array();
1205  
1206          /**

1207           * Store the fage status (true when opened, false when closed).

1208           * @access protected

1209           * @since 4.5.000 (2009-01-02)

1210           */
1211          protected $pageopen = array();
1212          
1213          /**

1214           * Default monospaced font

1215           * @access protected

1216           * @since 4.5.025 (2009-03-10)

1217           */
1218          protected $default_monospaced_font = 'courier';
1219  
1220          /**

1221           * Used to store a cloned copy of the current class object

1222           * @access protected

1223           * @since 4.5.029 (2009-03-19)

1224           */
1225          protected $objcopy;
1226  
1227          /**

1228           * Array used to store the lenghts of cache files

1229           * @access protected

1230           * @since 4.5.029 (2009-03-19)

1231           */
1232          protected $cache_file_lenght = array();
1233  
1234          /**

1235           * Table header content to be repeated on each new page

1236           * @access protected

1237           * @since 4.5.030 (2009-03-20)

1238           */
1239          protected $thead = '';
1240  
1241          /**

1242           * Distance between the top of page and end of table headers on a new page.

1243           * @access protected

1244           * @since 4.5.030 (2009-03-20)

1245           */
1246          protected $theadMargin = '';
1247  
1248          /**

1249           * Cache array for UTF8StringToArray() method.

1250           * @access protected

1251           * @since 4.5.037 (2009-04-07)

1252           */
1253          protected $cache_UTF8StringToArray = array();
1254  
1255          /**

1256           * Maximum size of cache array used for UTF8StringToArray() method.

1257           * @access protected

1258           * @since 4.5.037 (2009-04-07)

1259           */
1260          protected $cache_maxsize_UTF8StringToArray = 8;
1261  
1262          /**

1263           * Current size of cache array used for UTF8StringToArray() method.

1264           * @access protected

1265           * @since 4.5.037 (2009-04-07)

1266           */
1267          protected $cache_size_UTF8StringToArray = 0;
1268  
1269          /**

1270           * If true enables document signing

1271           * @access protected

1272           * @since 4.6.005 (2009-04-24)

1273           */
1274          protected $sign = false;
1275  
1276          /**

1277           * Signature data

1278           * @access protected

1279           * @since 4.6.005 (2009-04-24)

1280           */
1281          protected $signature_data = array();
1282  
1283          /**

1284           * Signature max lenght

1285           * @access protected

1286           * @since 4.6.005 (2009-04-24)

1287           */
1288          protected $signature_max_lenght = 5120;
1289  
1290          /**

1291           * Regular expression used to find blank characters used for word-wrapping.

1292           * @access protected

1293           * @since 4.6.006 (2009-04-28)

1294           */
1295          protected $re_spaces = '/[\s\p{Z}\p{Lo}]/';
1296  
1297          //------------------------------------------------------------

1298          // METHODS

1299          //------------------------------------------------------------

1300  
1301          /**

1302           * This is the class constructor. 

1303           * It allows to set up the page format, the orientation and 

1304           * the measure unit used in all the methods (except for the font sizes).

1305           * @since 1.0

1306           * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>

1307           * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.

1308           * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>

1309           * @param boolean $unicode TRUE means that the input text is unicode (default = true)

1310           * @param boolean $diskcache if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).

1311           * @param String $encoding charset encoding; default is UTF-8

1312           * @access public

1313           */
1314  		public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
1315              /* Set internal character encoding to ASCII */

1316              if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1317                  $this->internal_encoding = mb_internal_encoding();
1318                  mb_internal_encoding('ASCII');
1319              }
1320              // set disk caching

1321              $this->diskcache = $diskcache ? true : false;
1322              // set language direction

1323              $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
1324              $this->tmprtl = false;
1325              //Some checks

1326              $this->_dochecks();
1327              //Initialization of properties

1328              $this->isunicode = $unicode;
1329              $this->page = 0;
1330              $this->transfmrk[0] = array();
1331              $this->pagedim = array();
1332              $this->n = 2;
1333              $this->buffer = '';
1334              $this->pages = array();
1335              $this->state = 0;
1336              $this->fonts = array();
1337              $this->FontFiles = array();
1338              $this->diffs = array();
1339              $this->images = array();
1340              $this->links = array();
1341              $this->gradients = array();
1342              $this->InFooter = false;
1343              $this->lasth = 0;
1344              $this->FontFamily = 'helvetica';
1345              $this->FontStyle = '';
1346              $this->FontSizePt = 12;
1347              $this->underline = false;
1348              $this->linethrough = false;
1349              $this->DrawColor = '0 G';
1350              $this->FillColor = '0 g';
1351              $this->TextColor = '0 g';
1352              $this->ColorFlag = false;
1353              // encryption values

1354              $this->encrypted = false;
1355              $this->last_rc4_key = '';
1356              $this->padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
1357              //Standard Unicode fonts

1358              $this->CoreFonts = array(
1359                  'courier'=>'Courier',
1360                  'courierB'=>'Courier-Bold',
1361                  'courierI'=>'Courier-Oblique',
1362                  'courierBI'=>'Courier-BoldOblique',
1363                  'helvetica'=>'Helvetica',
1364                  'helveticaB'=>'Helvetica-Bold',
1365                  'helveticaI'=>'Helvetica-Oblique',
1366                  'helveticaBI'=>'Helvetica-BoldOblique',
1367                  'times'=>'Times-Roman',
1368                  'timesB'=>'Times-Bold',
1369                  'timesI'=>'Times-Italic',
1370                  'timesBI'=>'Times-BoldItalic',
1371                  'symbol'=>'Symbol',
1372                  'zapfdingbats'=>'ZapfDingbats'
1373              );
1374              //Set scale factor

1375              $this->setPageUnit($unit);
1376              // set page format and orientation

1377              $this->setPageFormat($format, $orientation);
1378              //Page margins (1 cm)

1379              $margin = 28.35 / $this->k;
1380              $this->SetMargins($margin, $margin);
1381              //Interior cell margin

1382              $this->cMargin = $margin / 10;
1383              //Line width (0.2 mm)

1384              $this->LineWidth = 0.57 / $this->k;
1385              $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
1386              $this->linestyleCap = '0 J';
1387              $this->linestyleJoin = '0 j';
1388              $this->linestyleDash = '[] 0 d';
1389              //Automatic page break

1390              $this->SetAutoPageBreak(true, (2 * $margin));
1391              //Full width display mode

1392              $this->SetDisplayMode('fullwidth');
1393              //Compression

1394              $this->SetCompression(true);
1395              //Set default PDF version number

1396              $this->PDFVersion = '1.7';
1397              $this->encoding = $encoding;
1398              $this->HREF = array();
1399              $this->getFontsList();
1400              $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1401              $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1402              $this->extgstates = array();
1403              // user's rights

1404              $this->sign = false;
1405              $this->ur = false;
1406              $this->ur_document = '/FullSave';
1407              $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export';
1408              $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1409              $this->ur_signature = '/Modify';            
1410              // set default JPEG quality

1411              $this->jpeg_quality = 75;
1412              // initialize some settings

1413              $this->utf8Bidi(array(''), '');
1414              // set default font

1415              $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1416              // check if PCRE Unicode support is enabled

1417              if (@preg_match('/\pL/u', 'a') == 1) {
1418                  // PCRE unicode support is turned ON

1419                  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.

1420                  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.

1421                  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.

1422                  $this->re_spaces = '/[\s\p{Z}\p{Lo}]/';
1423              } else {
1424                  // PCRE unicode support is turned OFF

1425                  $this->re_spaces = '/[\s]/';
1426              }
1427          }
1428          
1429          /**

1430           * Default destructor.

1431           * @access public

1432           * @since 1.53.0.TC016

1433           */
1434  		public function __destruct() {
1435              // restore internal encoding

1436              if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1437                  mb_internal_encoding($this->internal_encoding);
1438              }
1439              // unset all class variables

1440              $this->_destroy(true);
1441          }
1442          
1443          /**

1444           * Set the units of measure for the document.

1445           * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.

1446           * @access public

1447           * @since 3.0.015 (2008-06-06)

1448           */
1449  		public function setPageUnit($unit) {
1450          //Set scale factor

1451              switch (strtolower($unit)) {
1452                  // points

1453                  case 'px':
1454                  case 'pt': {
1455                      $this->k = 1;
1456                      break;
1457                  }
1458                  // millimeters

1459                  case 'mm': {
1460                      $this->k = $this->dpi / 25.4;
1461                      break;
1462                  }
1463                  // centimeters

1464                  case 'cm': {
1465                      $this->k = $this->dpi / 2.54;
1466                      break;
1467                  }
1468                  // inches

1469                  case 'in': {
1470                      $this->k = $this->dpi;
1471                      break;
1472                  }
1473                  // unsupported unit

1474                  default : {
1475                      $this->Error('Incorrect unit: '.$unit);
1476                      break;
1477                  }
1478              }
1479              if (isset($this->CurOrientation)) {
1480                      $this->setPageOrientation($this->CurOrientation);
1481              }
1482          }
1483          
1484          /**

1485          * Set the page format

1486          * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>

1487          * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>

1488          * @access public

1489          * @since 3.0.015 (2008-06-06)

1490          */
1491  		public function setPageFormat($format, $orientation='P') {
1492              //Page format

1493              if (is_string($format)) {
1494                  // Page formats (45 standard ISO paper formats and 4 american common formats).

1495                  // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)

1496                  switch (strtoupper($format)) {
1497                      case '4A0': {$format = array(4767.87,6740.79); break;}
1498                      case '2A0': {$format = array(3370.39,4767.87); break;}
1499                      case 'A0': {$format = array(2383.94,3370.39); break;}
1500                      case 'A1': {$format = array(1683.78,2383.94); break;}
1501                      case 'A2': {$format = array(1190.55,1683.78); break;}
1502                      case 'A3': {$format = array(841.89,1190.55); break;}
1503                      case 'A4': default: {$format = array(595.28,841.89); break;}
1504                      case 'A5': {$format = array(419.53,595.28); break;}
1505                      case 'A6': {$format = array(297.64,419.53); break;}
1506                      case 'A7': {$format = array(209.76,297.64); break;}
1507                      case 'A8': {$format = array(147.40,209.76); break;}
1508                      case 'A9': {$format = array(104.88,147.40); break;}
1509                      case 'A10': {$format = array(73.70,104.88); break;}
1510                      case 'B0': {$format = array(2834.65,4008.19); break;}
1511                      case 'B1': {$format = array(2004.09,2834.65); break;}
1512                      case 'B2': {$format = array(1417.32,2004.09); break;}
1513                      case 'B3': {$format = array(1000.63,1417.32); break;}
1514                      case 'B4': {$format = array(708.66,1000.63); break;}
1515                      case 'B5': {$format = array(498.90,708.66); break;}
1516                      case 'B6': {$format = array(354.33,498.90); break;}
1517                      case 'B7': {$format = array(249.45,354.33); break;}
1518                      case 'B8': {$format = array(175.75,249.45); break;}
1519                      case 'B9': {$format = array(124.72,175.75); break;}
1520                      case 'B10': {$format = array(87.87,124.72); break;}
1521                      case 'C0': {$format = array(2599.37,3676.54); break;}
1522                      case 'C1': {$format = array(1836.85,2599.37); break;}
1523                      case 'C2': {$format = array(1298.27,1836.85); break;}
1524                      case 'C3': {$format = array(918.43,1298.27); break;}
1525                      case 'C4': {$format = array(649.13,918.43); break;}
1526                      case 'C5': {$format = array(459.21,649.13); break;}
1527                      case 'C6': {$format = array(323.15,459.21); break;}
1528                      case 'C7': {$format = array(229.61,323.15); break;}
1529                      case 'C8': {$format = array(161.57,229.61); break;}
1530                      case 'C9': {$format = array(113.39,161.57); break;}
1531                      case 'C10': {$format = array(79.37,113.39); break;}
1532                      case 'RA0': {$format = array(2437.80,3458.27); break;}
1533                      case 'RA1': {$format = array(1729.13,2437.80); break;}
1534                      case 'RA2': {$format = array(1218.90,1729.13); break;}
1535                      case 'RA3': {$format = array(864.57,1218.90); break;}
1536                      case 'RA4': {$format = array(609.45,864.57); break;}
1537                      case 'SRA0': {$format = array(2551.18,3628.35); break;}
1538                      case 'SRA1': {$format = array(1814.17,2551.18); break;}
1539                      case 'SRA2': {$format = array(1275.59,1814.17); break;}
1540                      case 'SRA3': {$format = array(907.09,1275.59); break;}
1541                      case 'SRA4': {$format = array(637.80,907.09); break;}
1542                      case 'LETTER': {$format = array(612.00,792.00); break;}
1543                      case 'LEGAL': {$format = array(612.00,1008.00); break;}
1544                      case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1545                      case 'FOLIO': {$format = array(612.00,936.00); break;}
1546                  }
1547                  $this->fwPt = $format[0];
1548                  $this->fhPt = $format[1];
1549              } else {
1550                  $this->fwPt = $format[0] * $this->k;
1551                  $this->fhPt = $format[1] * $this->k;
1552              }
1553              $this->setPageOrientation($orientation);
1554          }
1555          
1556          /**

1557          * Set page orientation.

1558          * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>

1559          * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.

1560          * @param float $bottommargin bottom margin of the page.

1561          * @access public

1562          * @since 3.0.015 (2008-06-06)

1563          */
1564  		public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1565              $orientation = strtoupper($orientation);
1566              if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1567                  $this->CurOrientation = 'P';
1568                  $this->wPt = $this->fwPt;
1569                  $this->hPt = $this->fhPt;
1570              } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1571                  $this->CurOrientation = 'L';
1572                  $this->wPt = $this->fhPt;
1573                  $this->hPt = $this->fwPt;
1574              } else {
1575                  $this->Error('Incorrect orientation: '.$orientation);
1576              }
1577              $this->w = $this->wPt / $this->k;
1578              $this->h = $this->hPt / $this->k;
1579              if ($this->empty_string($autopagebreak)) {
1580                  if (isset($this->AutoPageBreak)) {
1581                      $autopagebreak = $this->AutoPageBreak;
1582                  } else {
1583                      $autopagebreak = true;
1584                  }
1585              }
1586              if ($this->empty_string($bottommargin)) {
1587                  if (isset($this->bMargin)) {
1588                      $bottommargin = $this->bMargin;
1589                  } else {
1590                      // default value = 2 cm

1591                      $bottommargin = 2 * 28.35 / $this->k;
1592                  }
1593              }
1594              $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1595              // store page dimensions

1596              $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'wk' => $this->w, 'hk' => $this->h, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation, 'olm' => $this->original_lMargin, 'orm' => $this->original_rMargin);
1597          }
1598                  
1599          /**

1600           * Enable or disable Right-To-Left language mode

1601           * @param Boolean $enable if true enable Right-To-Left language mode.

1602           * @access public

1603          * @since 2.0.000 (2008-01-03)

1604           */
1605  		public function setRTL($enable) {
1606              $this->rtl = $enable ? true : false;
1607              $this->tmprtl = false;
1608          }
1609          
1610          /**

1611           * Return the RTL status

1612           * @return boolean

1613           * @access public

1614           * @since 4.0.012 (2008-07-24)

1615           */
1616  		public function getRTL() {
1617              return $this->rtl;
1618          }
1619          
1620          /**

1621          * Force temporary RTL language direction

1622          * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL

1623          * @access public

1624          * @since 2.1.000 (2008-01-09)

1625          */
1626  		public function setTempRTL($mode) {
1627              switch ($mode) {
1628                  case false:
1629                  case 'L':
1630                  case 'R': {
1631                      $this->tmprtl = $mode;
1632                  }
1633              }
1634          }
1635          
1636          /**

1637          * Set the last cell height.

1638          * @param float $h cell height.

1639          * @author Nicola Asuni

1640          * @access public

1641          * @since 1.53.0.TC034

1642          */
1643  		public function setLastH($h) {
1644              $this->lasth = $h;
1645          }
1646          
1647          /**

1648          * Get the last cell height.

1649          * @return last cell height

1650          * @access public

1651          * @since 4.0.017 (2008-08-05)

1652          */
1653  		public function getLastH() {
1654              return $this->lasth;
1655          }
1656          
1657          /**

1658          * Set the adjusting factor to convert pixels to user units.

1659          * @param float $scale adjusting factor to convert pixels to user units.

1660          * @author Nicola Asuni

1661          * @access public

1662          * @since 1.5.2

1663          */
1664  		public function setImageScale($scale) {
1665              $this->imgscale = $scale;
1666          }
1667  
1668          /**

1669          * Returns the adjusting factor to convert pixels to user units.

1670          * @return float adjusting factor to convert pixels to user units.

1671          * @author Nicola Asuni

1672          * @access public

1673          * @since 1.5.2

1674          */
1675  		public function getImageScale() {
1676              return $this->imgscale;
1677          }
1678                  
1679          /**

1680          * Returns an array of page dimensions:

1681          * <ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul>

1682          * @param int $pagenum page number (empty = current page)

1683          * @return array of page dimensions.

1684          * @author Nicola Asuni

1685          * @access public

1686          * @since 4.5.027 (2009-03-16)

1687          */
1688  		public function getPageDimensions($pagenum='') {
1689              if (empty($pagenum)) {
1690                  $pagenum = $this->page;
1691              }
1692              return $this->pagedim[$pagenum];
1693          }
1694          
1695          /**

1696          * Returns the page width in units.

1697          * @param int $pagenum page number (empty = current page)

1698          * @return int page width.

1699          * @author Nicola Asuni

1700          * @access public

1701          * @since 1.5.2

1702          * @see getPageDimensions()

1703          */
1704  		public function getPageWidth($pagenum='') {
1705              if (empty($pagenum)) {
1706                  return $this->w;
1707              }
1708              return $this->pagedim[$pagenum]['w'];
1709          }
1710  
1711          /**

1712          * Returns the page height in units.

1713          * @param int $pagenum page number (empty = current page)

1714          * @return int page height.

1715          * @author Nicola Asuni

1716          * @access public

1717          * @since 1.5.2

1718          * @see getPageDimensions()

1719          */
1720  		public function getPageHeight($pagenum='') {
1721              if (empty($pagenum)) {
1722                  return $this->h;
1723              }
1724              return $this->pagedim[$pagenum]['h'];
1725          }
1726  
1727          /**

1728          * Returns the page break margin.

1729          * @param int $pagenum page number (empty = current page)

1730          * @return int page break margin.

1731          * @author Nicola Asuni

1732          * @access public

1733          * @since 1.5.2

1734          * @see getPageDimensions()

1735          */
1736  		public function getBreakMargin($pagenum='') {
1737              if (empty($pagenum)) {
1738                  return $this->bMargin;
1739              }
1740              return $this->pagedim[$pagenum]['bm'];
1741          }
1742  
1743          /**

1744          * Returns the scale factor (number of points in user unit).

1745          * @return int scale factor.

1746          * @author Nicola Asuni

1747          * @access public

1748          * @since 1.5.2

1749          */
1750  		public function getScaleFactor() {
1751              return $this->k;
1752          }
1753  
1754          /**

1755          * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.

1756          * @param float $left Left margin.

1757          * @param float $top Top margin.

1758          * @param float $right Right margin. Default value is the left one.

1759          * @access public

1760          * @since 1.0

1761          * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()

1762          */
1763  		public function SetMargins($left, $top, $right=-1) {
1764              //Set left, top and right margins

1765              $this->lMargin = $left;
1766              $this->tMargin = $top;
1767              if ($right == -1) {
1768                  $right = $left;
1769              }
1770              $this->rMargin = $right;
1771          }
1772  
1773          /**

1774          * 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.

1775          * @param float $margin The margin.

1776          * @access public

1777          * @since 1.4

1778          * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()

1779          */
1780  		public function SetLeftMargin($margin) {
1781              //Set left margin

1782              $this->lMargin=$margin;
1783              if (($this->page > 0) AND ($this->x < $margin)) {
1784                  $this->x = $margin;
1785              }
1786          }
1787  
1788          /**

1789          * Defines the top margin. The method can be called before creating the first page.

1790          * @param float $margin The margin.

1791          * @access public

1792          * @since 1.5

1793          * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()

1794          */
1795  		public function SetTopMargin($margin) {
1796              //Set top margin

1797              $this->tMargin=$margin;
1798              if (($this->page > 0) AND ($this->y < $margin)) {
1799                  $this->y = $margin;
1800              }
1801          }
1802  
1803          /**

1804          * Defines the right margin. The method can be called before creating the first page.

1805          * @param float $margin The margin.

1806          * @access public

1807          * @since 1.5

1808          * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()

1809          */
1810  		public function SetRightMargin($margin) {
1811              $this->rMargin=$margin;
1812              if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1813                  $this->x = $this->w - $margin;
1814              }
1815          }
1816  
1817          /**

1818          * Set the internal Cell padding.

1819          * @param float $pad internal padding.

1820          * @access public

1821          * @since 2.1.000 (2008-01-09)

1822          * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()

1823          */
1824  		public function SetCellPadding($pad) {
1825              $this->cMargin = $pad;
1826          }
1827  
1828          /**

1829          * 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.

1830          * @param boolean $auto Boolean indicating if mode should be on or off.

1831          * @param float $margin Distance from the bottom of the page.

1832          * @access public

1833          * @since 1.0

1834          * @see Cell(), MultiCell(), AcceptPageBreak()

1835          */
1836  		public function SetAutoPageBreak($auto, $margin=0) {
1837              //Set auto page break mode and triggering margin

1838              $this->AutoPageBreak = $auto;
1839              $this->bMargin = $margin;
1840              $this->PageBreakTrigger = $this->h - $margin;
1841          }
1842  
1843          /**

1844          * Defines the way the document is to be displayed by the viewer.

1845          * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul>

1846          * @param string $layout The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul>

1847          * @param string $mode A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul>

1848          * @access public

1849          * @since 1.2

1850          */
1851  		public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
1852              //Set display mode in viewer

1853              if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1854                  $this->ZoomMode = $zoom;
1855              } else {
1856                  $this->Error('Incorrect zoom display mode: '.$zoom);
1857              }
1858              switch ($layout) {
1859                  case 'default':
1860                  case 'single':
1861                  case 'SinglePage': {
1862                      $this->LayoutMode = 'SinglePage';
1863                      break;
1864                  }
1865                  case 'continuous':
1866                  case 'OneColumn': {
1867                      $this->LayoutMode = 'OneColumn';
1868                      break;
1869                  }
1870                  case 'two':
1871                  case 'TwoColumnLeft': {
1872                      $this->LayoutMode = 'TwoColumnLeft';
1873                      break;
1874                  }
1875                  case 'TwoColumnRight': {
1876                      $this->LayoutMode = 'TwoColumnRight';
1877                      break;
1878                  }
1879                  case 'TwoPageLeft': {
1880                      $this->LayoutMode = 'TwoPageLeft';
1881                      break;
1882                  }
1883                  case 'TwoPageRight': {
1884                      $this->LayoutMode = 'TwoPageRight';
1885                      break;
1886                  }
1887                  default: {
1888                      $this->LayoutMode = 'SinglePage';
1889                  }
1890              }
1891              switch ($mode) {
1892                  case 'UseNone': {
1893                      $this->PageMode = 'UseNone';
1894                      break;
1895                  }
1896                  case 'UseOutlines': {
1897                      $this->PageMode = 'UseOutlines';
1898                      break;
1899                  }
1900                  case 'UseThumbs': {
1901                      $this->PageMode = 'UseThumbs';
1902                      break;
1903                  }
1904                  case 'FullScreen': {
1905                      $this->PageMode = 'FullScreen';
1906                      break;
1907                  }
1908                  case 'UseOC': {
1909                      $this->PageMode = 'UseOC';
1910                      break;
1911                  }
1912                  case '': {
1913                      $this->PageMode = 'UseAttachments';
1914                      break;
1915                  }
1916                  default: {
1917                      $this->PageMode = 'UseNone';
1918                  }
1919              }
1920          }
1921  
1922          /**

1923          * 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.

1924          * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.

1925          * @param boolean $compress Boolean indicating if compression must be enabled.

1926          * @access public

1927          * @since 1.4

1928          */
1929  		public function SetCompression($compress) {
1930              //Set page compression

1931              if (function_exists('gzcompress')) {
1932                  $this->compress = $compress;
1933              } else {
1934                  $this->compress = false;
1935              }
1936          }
1937  
1938          /**

1939          * Defines the title of the document.

1940          * @param string $title The title.

1941          * @access public

1942          * @since 1.2

1943          * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()

1944          */
1945  		public function SetTitle($title) {
1946              //Title of document

1947              $this->title = $title;
1948          }
1949  
1950          /**

1951          * Defines the subject of the document.

1952          * @param string $subject The subject.

1953          * @access public

1954          * @since 1.2

1955          * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()

1956          */
1957  		public function SetSubject($subject) {
1958              //Subject of document

1959              $this->subject = $subject;
1960          }
1961  
1962          /**

1963          * Defines the author of the document.

1964          * @param string $author The name of the author.

1965          * @access public

1966          * @since 1.2

1967          * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()

1968          */
1969  		public function SetAuthor($author) {
1970              //Author of document

1971              $this->author = $author;
1972          }
1973  
1974          /**

1975          * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.

1976          * @param string $keywords The list of keywords.

1977          * @access public

1978          * @since 1.2

1979          * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()

1980          */
1981  		public function SetKeywords($keywords) {
1982              //Keywords of document

1983              $this->keywords = $keywords;
1984          }
1985  
1986          /**

1987          * Defines the creator of the document. This is typically the name of the application that generates the PDF.

1988          * @param string $creator The name of the creator.

1989          * @access public

1990          * @since 1.2

1991          * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()

1992          */
1993  		public function SetCreator($creator) {
1994              //Creator of document

1995              $this->creator = $creator;
1996          }
1997          
1998          /**

1999          * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid.

2000          * 2004-06-11 :: Nicola Asuni : changed bold tag with strong

2001          * @param string $msg The error message

2002          * @access public

2003          * @since 1.0

2004          */
2005  		public function Error($msg) {
2006              // unset all class variables

2007              $this->_destroy(true);
2008              // exit program and print error

2009              die('<strong>TCPDF ERROR: </strong>'.$msg);
2010          }
2011  
2012          /**

2013          * This method begins the generation of the PDF document.

2014          * It is not necessary to call it explicitly because AddPage() does it automatically.

2015          * Note: no page is created by this method

2016          * @access public

2017          * @since 1.0

2018          * @see AddPage(), Close()

2019          */
2020  		public function Open() {
2021              //Begin document

2022              $this->state = 1;
2023          }
2024  
2025          /**

2026          * Terminates the PDF document.

2027          * It is not necessary to call this method explicitly because Output() does it automatically.

2028          * If the document contains no page, AddPage() is called to prevent from getting an invalid document.

2029          * @access public

2030          * @since 1.0

2031          * @see Open(), Output()

2032          */
2033  		public function Close() {
2034              if ($this->state == 3) {
2035                  return;
2036              }
2037              if ($this->page == 0) {
2038                  $this->AddPage();
2039              }
2040              // close page

2041              $this->endPage();
2042              // close document

2043              $this->_enddoc();
2044              // unset all class variables (except critical ones)

2045              $this->_destroy(false);
2046          }
2047          
2048          /**

2049          * Move pointer at the specified document page and update page dimensions.

2050          * @param int $pnum page number

2051          * @param boolean $resetmargins if true reset left, right, top margins and Y position.

2052          * @access public

2053          * @since 2.1.000 (2008-01-07)

2054          * @see getPage(), lastpage(), getNumPages()

2055          */
2056  		public function setPage($pnum, $resetmargins=false) {
2057              if ($pnum == $this->page) {
2058                  return;
2059              }
2060              if (($pnum > 0) AND ($pnum <= $this->numpages)) {
2061                  $this->state = 2;
2062                  // save current graphic settings

2063                  //$gvars = $this->getGraphicVars();

2064                  $oldpage = $this->page;
2065                  $this->page = $pnum;
2066                  $this->wPt = $this->pagedim[$this->page]['w'];
2067                  $this->hPt = $this->pagedim[$this->page]['h'];
2068                  $this->w = $this->wPt / $this->k;
2069                  $this->h = $this->hPt / $this->k;
2070                  $this->tMargin = $this->pagedim[$this->page]['tm'];
2071                  $this->bMargin = $this->pagedim[$this->page]['bm'];
2072                  $this->original_lMargin = $this->pagedim[$this->page]['olm'];
2073                  $this->original_rMargin = $this->pagedim[$this->page]['orm'];
2074                  $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
2075                  $this->CurOrientation = $this->pagedim[$this->page]['or'];
2076                  $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
2077                  // restore graphic settings

2078                  //$this->setGraphicVars($gvars);

2079                  if ($resetmargins) {
2080                      $this->lMargin = $this->pagedim[$this->page]['olm'];
2081                      $this->rMargin = $this->pagedim[$this->page]['orm'];
2082                      $this->SetY($this->tMargin);
2083                  } else {
2084                      // account for booklet mode

2085                      if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
2086                          $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
2087                          $this->lMargin += $deltam;
2088                          $this->rMargin -= $deltam;
2089                      }
2090                  }
2091              } else {
2092                  $this->Error('Wrong page number on setPage() function.');
2093              }
2094          }
2095          
2096          /**

2097          * Reset pointer to the last document page.

2098          * @param boolean $resetmargins if true reset left, right, top margins and Y position.

2099          * @access public

2100          * @since 2.0.000 (2008-01-04)

2101          * @see setPage(), getPage(), getNumPages()

2102          */
2103  		public function lastPage($resetmargins=false) {
2104              $this->setPage($this->getNumPages(), $resetmargins);
2105          }
2106          
2107          /**

2108          * Get current document page number.

2109          * @return int page number

2110          * @access public

2111          * @since 2.1.000 (2008-01-07)

2112          * @see setPage(), lastpage(), getNumPages()

2113          */
2114  		public function getPage() {
2115              return $this->page;
2116          }
2117          
2118          
2119          /**

2120          * Get the total number of insered pages.

2121          * @return int number of pages

2122          * @access public

2123          * @since 2.1.000 (2008-01-07)

2124          * @see setPage(), getPage(), lastpage()

2125          */
2126  		public function getNumPages() {
2127              return $this->numpages;
2128          }
2129  
2130          /**

2131          * 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).

2132          * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.

2133          * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>

2134          * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>

2135          * @access public

2136          * @since 1.0

2137          * @see startPage(), endPage()

2138          */
2139  		public function AddPage($orientation='', $format='') {
2140              if (!isset($this->original_lMargin)) {
2141                  $this->original_lMargin = $this->lMargin;
2142              }
2143              if (!isset($this->original_rMargin)) {
2144                  $this->original_rMargin = $this->rMargin;
2145              }
2146              // terminate previous page

2147              $this->endPage();
2148              // start new page

2149              $this->startPage($orientation, $format);
2150          }
2151  
2152          /**

2153          * Terminate the current page

2154          * @access protected

2155          * @since 4.2.010 (2008-11-14)

2156          * @see startPage(), AddPage()

2157          */
2158  		protected function endPage() {
2159              // check if page is already closed

2160              if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
2161                  return;
2162              }
2163              $this->InFooter = true;
2164              // print page footer

2165              $this->setFooter();
2166              // close page

2167              $this->_endpage();
2168              // mark page as closed

2169              $this->pageopen[$this->page] = false;
2170              $this->InFooter = false;
2171          }
2172  
2173          /**

2174          * Starts a new page to the document. The page must be closed using the endPage() function.

2175          * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.

2176          * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>

2177          * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>

2178          * @access protected

2179          * @since 4.2.010 (2008-11-14)

2180          * @see endPage(), AddPage()

2181          */
2182  		protected function startPage($orientation='', $format='') {
2183              if ($this->numpages > $this->page) {
2184                  // this page has been already added

2185                  $this->setPage($this->page + 1);
2186                  $this->SetY($this->tMargin);
2187                  return;
2188              }
2189              // start a new page

2190              if ($this->state == 0) {
2191                  $this->Open();
2192              }
2193              ++$this->numpages;
2194              $this->swapMargins($this->booklet);
2195              // save current graphic settings

2196              $gvars = $this->getGraphicVars();
2197              // start new page

2198              $this->_beginpage($orientation, $format);
2199              // mark page as open

2200              $this->pageopen[$this->page] = true;
2201              // restore graphic settings

2202              $this->setGraphicVars($gvars);
2203              // mark this point

2204              $this->setPageMark();
2205              // print page header

2206              $this->setHeader();
2207              // restore graphic settings

2208              $this->setGraphicVars($gvars);
2209              // mark this point

2210              $this->setPageMark();
2211              // print table header (if any)

2212              $this->setTableHeader();
2213          }
2214              
2215          /**

2216            * Set start-writing mark on current page for multicell borders and fills.

2217            * This function must be called after calling Image() function for a background image.

2218            * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.

2219            * @access public

2220            * @since 4.0.016 (2008-07-30)

2221           */
2222  		public function setPageMark() {
2223              $this->intmrk[$this->page] = $this->pagelen[$this->page];
2224          }
2225          
2226          /**

2227            * Set header data.

2228           * @param string $ln header image logo

2229           * @param string $lw header image logo width in mm

2230           * @param string $ht string to print as title on document header

2231           * @param string $hs string to print on document header

2232           * @access public

2233           */
2234  		public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
2235              $this->header_logo = $ln;
2236              $this->header_logo_width = $lw;
2237              $this->header_title = $ht;
2238              $this->header_string = $hs;
2239          }
2240          
2241          /**

2242            * Returns header data:

2243            * <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>

2244           * @return array()

2245           * @access public

2246           * @since 4.0.012 (2008-07-24)

2247           */
2248  		public function getHeaderData() {
2249              $ret = array();
2250              $ret['logo'] = $this->header_logo;
2251              $ret['logo_width'] = $this->header_logo_width;
2252              $ret['title'] = $this->header_title;
2253              $ret['string'] = $this->header_string;
2254              return $ret;
2255          }
2256          
2257          /**

2258            * Set header margin.

2259           * (minimum distance between header and top page margin)

2260           * @param int $hm distance in user units

2261           * @access public

2262           */
2263  		public function setHeaderMargin($hm=10) {
2264              $this->header_margin = $hm;
2265          }
2266          
2267          /**

2268            * Returns header margin in user units.

2269           * @return float

2270           * @since 4.0.012 (2008-07-24)

2271           * @access public

2272           */
2273  		public function getHeaderMargin() {
2274              return $this->header_margin;
2275          }
2276          
2277          /**

2278            * Set footer margin.

2279           * (minimum distance between footer and bottom page margin)

2280           * @param int $fm distance in user units

2281           * @access public

2282           */
2283  		public function setFooterMargin($fm=10) {
2284              $this->footer_margin = $fm;
2285          }
2286          
2287          /**

2288            * Returns footer margin in user units.

2289           * @return float

2290           * @since 4.0.012 (2008-07-24)

2291           * @access public

2292           */
2293  		public function getFooterMargin() {
2294              return $this->footer_margin;
2295          }
2296          /**

2297            * Set a flag to print page header.

2298           * @param boolean $val set to true to print the page header (default), false otherwise. 

2299           * @access public

2300           */
2301  		public function setPrintHeader($val=true) {
2302              $this->print_header = $val;
2303          }
2304          
2305          /**

2306            * Set a flag to print page footer.

2307           * @param boolean $value set to true to print the page footer (default), false otherwise. 

2308           * @access public

2309           */
2310  		public function setPrintFooter($val=true) {
2311              $this->print_footer = $val;
2312          }
2313          
2314          /**

2315            * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image

2316           * @return float 

2317           * @access public

2318           */
2319  		public function getImageRBX() {
2320              return $this->img_rb_x;
2321          }
2322          
2323          /**

2324            * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image

2325           * @return float 

2326           * @access public

2327           */
2328  		public function getImageRBY() {
2329              return $this->img_rb_y;
2330          }
2331          
2332          /**

2333            * This method is used to render the page header.

2334            * It is automatically called by AddPage() and could be overwritten in your own inherited class.

2335           * @access public

2336           */
2337  		public function Header() {
2338              $ormargins = $this->getOriginalMargins();
2339              $headerfont = $this->getHeaderFont();
2340              $headerdata = $this->getHeaderData();
2341              if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2342                  $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2343                  $imgy = $this->getImageRBY();
2344              } else {
2345                  $imgy = $this->GetY();
2346              }
2347              $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2348              // set starting margin for text data cell

2349              if ($this->getRTL()) {
2350                  $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2351              } else {
2352                  $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2353              }
2354              $this->SetTextColor(0, 0, 0);
2355              // header title

2356              $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2357              $this->SetX($header_x);            
2358              $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
2359              // header string

2360              $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2361              $this->SetX($header_x);
2362              $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
2363              // print an ending header line

2364              $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2365              $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
2366              if ($this->getRTL()) {
2367                  $this->SetX($ormargins['right']);
2368              } else {
2369                  $this->SetX($ormargins['left']);
2370              }
2371              $this->Cell(0, 0, '', 'T', 0, 'C');
2372          }
2373          
2374          /**

2375            * This method is used to render the page footer. 

2376            * It is automatically called by AddPage() and could be overwritten in your own inherited class.

2377           * @access public

2378           */
2379  		public function Footer() {                
2380              $cur_y = $this->GetY();
2381              $ormargins = $this->getOriginalMargins();
2382              $this->SetTextColor(0, 0, 0);            
2383              //set style for cell border

2384              $line_width = 0.85 / $this->getScaleFactor();
2385              $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2386              //print document barcode

2387              $barcode = $this->getBarcode();
2388              if (!empty($barcode)) {
2389                  $this->Ln($line_width);
2390                  $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2391                  $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');    
2392              }
2393              if (empty($this->pagegroups)) {
2394                  $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
2395              } else {
2396                  $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
2397              }        
2398              $this->SetY($cur_y);
2399              //Print page number

2400              if ($this->getRTL()) {
2401                  $this->SetX($ormargins['right']);
2402                  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2403              } else {
2404                  $this->SetX($ormargins['left']);
2405                  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2406              }
2407          }
2408          
2409          /**

2410            * This method is used to render the page header. 

2411            * @access protected

2412            * @since 4.0.012 (2008-07-24)

2413           */
2414  		protected function setHeader() {
2415              if ($this->print_header) {
2416                  $lasth = $this->lasth;
2417                  $this->_out('q');
2418                  $this->rMargin = $this->original_rMargin;
2419                  $this->lMargin = $this->original_lMargin;
2420                  $this->cMargin = 0;
2421                  //set current position

2422                  if ($this->rtl) {
2423                      $this->SetXY($this->original_rMargin, $this->header_margin);
2424                  } else {
2425                      $this->SetXY($this->original_lMargin, $this->header_margin);
2426                  }
2427                  $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2428                  $this->Header();
2429                  //restore position

2430                  if ($this->rtl) {
2431                      $this->SetXY($this->original_rMargin, $this->tMargin);
2432                  } else {
2433                      $this->SetXY($this->original_lMargin, $this->tMargin);
2434                  }
2435                  $this->_out('Q');
2436                  $this->lasth = $lasth;
2437              }
2438          }
2439          
2440          /**

2441            * This method is used to render the page footer. 

2442            * @access protected

2443            * @since 4.0.012 (2008-07-24)

2444           */
2445  		protected function setFooter() {
2446              //Page footer

2447              // save current graphic settings

2448              $gvars = $this->getGraphicVars();
2449              // mark this point

2450              $this->footerpos[$this->page] = $this->pagelen[$this->page];
2451              $this->_out("\n");
2452              if ($this->print_footer) {
2453                  $lasth = $this->lasth;
2454                  $this->_out('q');
2455                  $this->rMargin = $this->original_rMargin;
2456                  $this->lMargin = $this->original_lMargin;
2457                  $this->cMargin = 0;
2458                  //set current position

2459                  $footer_y = $this->h - $this->footer_margin;
2460                  if ($this->rtl) {
2461                      $this->SetXY($this->original_rMargin, $footer_y);
2462                  } else {
2463                      $this->SetXY($this->original_lMargin, $footer_y);
2464                  }
2465                  $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
2466                  $this->Footer();
2467                  //restore position

2468                  if ($this->rtl) {
2469                      $this->SetXY($this->original_rMargin, $this->tMargin);
2470                  } else {
2471                      $this->SetXY($this->original_lMargin, $this->tMargin);
2472                  }
2473                  $this->_out('Q');
2474                  $this->lasth = $lasth;
2475              }
2476              // restore graphic settings

2477              $this->setGraphicVars($gvars);
2478              // calculate footer lenght

2479              $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
2480          }
2481  
2482          /**

2483            * This method is used to render the table header on new page (if any). 

2484            * @access protected

2485            * @since 4.5.030 (2009-03-25)

2486           */
2487  		protected function setTableHeader() {
2488              if (!$this->empty_string($this->theadMargin)) {
2489                  // restore the original top-margin

2490                  $this->tMargin = $this->theadMargin;
2491                  $this->pagedim[$this->page]['tm'] = $this->theadMargin;
2492                  $this->y = $this->theadMargin;
2493              }
2494              if (!$this->empty_string($this->thead)) {
2495                  // print table header

2496                  $this->writeHTML($this->thead, false, false, false, false, '');
2497                  // set new top margin to skip the table headers

2498                  if (!isset($this->theadMargin) OR ($this->empty_string($this->theadMargin))) {
2499                      $this->theadMargin = $this->tMargin;
2500                  }
2501                  $this->tMargin = $this->y;
2502                  $this->pagedim[$this->page]['tm'] = $this->tMargin;
2503              }
2504          }
2505          
2506          /**

2507          * Returns the current page number.

2508          * @return int page number

2509          * @access public

2510          * @since 1.0

2511          * @see AliasNbPages(), getAliasNbPages()

2512          */
2513  		public function PageNo() {
2514              return $this->page;
2515          }
2516  
2517          /**

2518          * Defines a new spot color. 

2519          * It can be expressed in RGB components or gray scale. 

2520          * The method can be called before the first page is created and the value is retained from page to page.

2521          * @param int $c Cyan color for CMYK. Value between 0 and 255

2522          * @param int $m Magenta color for CMYK. Value between 0 and 255

2523          * @param int $y Yellow color for CMYK. Value between 0 and 255

2524          * @param int $k Key (Black) color for CMYK. Value between 0 and 255

2525          * @access public

2526          * @since 4.0.024 (2008-09-12)

2527          * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()

2528          */
2529  		public function AddSpotColor($name, $c, $m, $y, $k) {
2530              if (!isset($this->spot_colors[$name])) {
2531                  $i = 1 + count($this->spot_colors);
2532                  $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2533              }
2534          }
2535  
2536          /**

2537          * Defines the color used for all drawing operations (lines, rectangles and cell borders). 

2538          * It can be expressed in RGB components or gray scale. 

2539          * The method can be called before the first page is created and the value is retained from page to page.

2540          * @param array $color array of colors

2541          * @access public

2542          * @since 3.1.000 (2008-06-11)

2543          * @see SetDrawColor()

2544          */
2545  		public function SetDrawColorArray($color) {
2546              if (isset($color)) {
2547                  $color = array_values($color);
2548                  $r = isset($color[0]) ? $color[0] : -1;
2549                  $g = isset($color[1]) ? $color[1] : -1;
2550                  $b = isset($color[2]) ? $color[2] : -1;
2551                  $k = isset($color[3]) ? $color[3] : -1;
2552                  if ($r >= 0) {
2553                      $this->SetDrawColor($r, $g, $b, $k);
2554                  }
2555              }
2556          }
2557  
2558          /**

2559          * 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.

2560          * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255

2561          * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255

2562          * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255

2563          * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255

2564          * @access public

2565          * @since 1.3

2566          * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()

2567          */
2568  		public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2569              // set default values

2570              if (!is_numeric($col1)) {
2571                  $col1 = 0;
2572              }
2573              if (!is_numeric($col2)) {
2574                  $col2 = -1;
2575              }
2576              if (!is_numeric($col3)) {
2577                  $col3 = -1;
2578              }
2579              if (!is_numeric($col4)) {
2580                  $col4 = -1;
2581              }
2582              //Set color for all stroking operations

2583              if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2584                  // Grey scale

2585                  $this->DrawColor = sprintf('%.3F G', $col1/255);
2586              } elseif ($col4 == -1) {
2587                  // RGB

2588                  $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
2589              } else {
2590                  // CMYK

2591                  $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
2592              }
2593              if ($this->page > 0) {
2594                  $this->_out($this->DrawColor);
2595              }
2596          }
2597          
2598          /**

2599          * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).

2600          * @param string $name name of the spot color

2601          * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).

2602          * @access public

2603          * @since 4.0.024 (2008-09-12)

2604          * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()

2605          */
2606  		public function SetDrawSpotColor($name, $tint=100) {
2607              if (!isset($this->spot_colors[$name])) {
2608                  $this->Error('Undefined spot color: '.$name);
2609              }
2610              $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
2611              if ($this->page > 0) {
2612                  $this->_out($this->DrawColor);
2613              }
2614          }
2615          
2616          /**

2617          * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 

2618          * It can be expressed in RGB components or gray scale. 

2619          * The method can be called before the first page is created and the value is retained from page to page.

2620          * @param array $color array of colors

2621          * @access public

2622          * @since 3.1.000 (2008-6-11)

2623          * @see SetFillColor()

2624          */
2625  		public function SetFillColorArray($color) {
2626              if (isset($color)) {
2627                  $color = array_values($color);
2628                  $r = isset($color[0]) ? $color[0] : -1;
2629                  $g = isset($color[1]) ? $color[1] : -1;
2630                  $b = isset($color[2]) ? $color[2] : -1;
2631                  $k = isset($color[3]) ? $color[3] : -1;
2632                  if ($r >= 0) {
2633                      $this->SetFillColor($r, $g, $b, $k);
2634                  }
2635              }
2636          }
2637          
2638          /**

2639          * 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.

2640          * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255

2641          * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255

2642          * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255

2643          * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255

2644          * @access public

2645          * @since 1.3

2646          * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()

2647          */
2648  		public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2649              // set default values

2650              if (!is_numeric($col1)) {
2651                  $col1 = 0;
2652              }
2653              if (!is_numeric($col2)) {
2654                  $col2 = -1;
2655              }
2656              if (!is_numeric($col3)) {
2657                  $col3 = -1;
2658              }
2659              if (!is_numeric($col4)) {
2660                  $col4 = -1;
2661              }
2662              //Set color for all filling operations

2663              if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2664                  // Grey scale

2665                  $this->FillColor = sprintf('%.3F g', $col1/255);
2666                  $this->bgcolor = array('G' => $col1);
2667              } elseif ($col4 == -1) {
2668                  // RGB

2669                  $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2670                  $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2671              } else {
2672                  // CMYK

2673                  $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2674                  $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2675              }
2676              $this->ColorFlag = ($this->FillColor != $this->TextColor);
2677              if ($this->page > 0) {
2678                  $this->_out($this->FillColor);
2679              }
2680          }
2681          
2682          /**

2683          * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).

2684          * @param string $name name of the spot color

2685          * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).

2686          * @access public

2687          * @since 4.0.024 (2008-09-12)

2688          * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()

2689          */
2690  		public function SetFillSpotColor($name, $tint=100) {
2691              if (!isset($this->spot_colors[$name])) {
2692                  $this->Error('Undefined spot color: '.$name);
2693              }
2694              $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2695              $this->ColorFlag = ($this->FillColor != $this->TextColor);
2696              if ($this->page > 0) {
2697                  $this->_out($this->FillColor);
2698              }
2699          }
2700          
2701          /**

2702          * Defines the color used for text. It can be expressed in RGB components or gray scale. 

2703          * The method can be called before the first page is created and the value is retained from page to page.

2704          * @param array $color array of colors

2705          * @access public

2706          * @since 3.1.000 (2008-6-11)

2707          * @see SetFillColor()

2708          */
2709  		public function SetTextColorArray($color) {
2710              if (isset($color)) {
2711                  $color = array_values($color);
2712                  $r = isset($color[0]) ? $color[0] : -1;
2713                  $g = isset($color[1]) ? $color[1] : -1;
2714                  $b = isset($color[2]) ? $color[2] : -1;
2715                  $k = isset($color[3]) ? $color[3] : -1;
2716                  if ($r >= 0) {
2717                      $this->SetTextColor($r, $g, $b, $k);
2718                  }
2719              }
2720          }
2721  
2722          /**

2723          * 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.

2724          * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255

2725          * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255

2726          * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255

2727          * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255

2728          * @access public

2729          * @since 1.3

2730          * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()

2731          */
2732  		public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2733              // set default values

2734              if (!is_numeric($col1)) {
2735                  $col1 = 0;
2736              }
2737              if (!is_numeric($col2)) {
2738                  $col2 = -1;
2739              }
2740              if (!is_numeric($col3)) {
2741                  $col3 = -1;
2742              }
2743              if (!is_numeric($col4)) {
2744                  $col4 = -1;
2745              }
2746              //Set color for text

2747              if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2748                  // Grey scale

2749                  $this->TextColor = sprintf('%.3F g', $col1/255);
2750                  $this->fgcolor = array('G' => $col1);
2751              } elseif ($col4 == -1) {
2752                  // RGB

2753                  $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2754                  $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2755              } else {
2756                  // CMYK

2757                  $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2758                  $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2759              }
2760              $this->ColorFlag = ($this->FillColor != $this->TextColor);
2761          }
2762          
2763          /**

2764          * Defines the spot color used for text.

2765          * @param string $name name of the spot color

2766          * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).

2767          * @access public

2768          * @since 4.0.024 (2008-09-12)

2769          * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()

2770          */
2771  		public function SetTextSpotColor($name, $tint=100) {
2772              if (!isset($this->spot_colors[$name])) {
2773                  $this->Error('Undefined spot color: '.$name);
2774              }
2775              $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2776              $this->ColorFlag = ($this->FillColor != $this->TextColor);
2777              if ($this->page > 0) {
2778                  $this->_out($this->TextColor);
2779              }
2780          }
2781  
2782          /**

2783          * Returns the length of a string in user unit. A font must be selected.<br>

2784          * @param string $s The string whose length is to be computed

2785          * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.

2786          * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular.

2787          * @param float $fontsize Font size in points. The default value is the current size.

2788          * @return int string length

2789          * @author Nicola Asuni

2790          * @access public

2791          * @since 1.2

2792          */
2793  		public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2794              return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize);
2795          }
2796          
2797          /**

2798          * Returns the string length of an array of chars in user unit. A font must be selected.<br>

2799          * @param string $arr The array of chars whose total length is to be computed

2800          * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.

2801          * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular.

2802          * @param float $fontsize Font size in points. The default value is the current size.

2803          * @return int string length

2804          * @author Nicola Asuni

2805          * @access public

2806          * @since 2.4.000 (2008-03-06)

2807          */
2808  		public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2809              // store current values

2810              if (!$this->empty_string($fontname)) {
2811                  $prev_FontFamily = $this->FontFamily;
2812                  $prev_FontStyle = $this->FontStyle;
2813                  $prev_FontSizePt = $this->FontSizePt;
2814                  $this->SetFont($fontname, $fontstyle, $fontsize);
2815              }
2816              $w = 0;
2817              foreach ($sa as $char) {
2818                  $w += $this->GetCharWidth($char);
2819              }
2820              // restore previous values

2821              if (!$this->empty_string($fontname)) {
2822                  $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2823              }
2824              return $w;
2825          }
2826          
2827          /**

2828          * Returns the length of the char in user unit for the current font.<br>

2829          * @param int $char The char code whose length is to be returned

2830          * @return int char width

2831          * @author Nicola Asuni

2832          * @access public

2833          * @since 2.4.000 (2008-03-06)

2834          */
2835  		public function GetCharWidth($char) {
2836              if ($char == 173) {
2837                  // SHY character will not be printed

2838                  return (0);
2839              }
2840              $cw = &$this->CurrentFont['cw'];
2841              if (isset($cw[$char])) {
2842                  $w = $cw[$char];
2843              } elseif (isset($this->CurrentFont['dw'])) {
2844                  // default width

2845                  $w = $this->CurrentFont['dw'];
2846              } elseif (isset($cw[32])) {
2847                  // default width

2848                  $dw = $cw[32];
2849              } else {
2850                  $w = 600;
2851              }
2852              return ($w * $this->FontSize / 1000);
2853          }
2854          
2855          /**

2856          * Returns the numbero of characters in a string.

2857          * @param string $s The input string.

2858          * @return int number of characters

2859          * @access public

2860          * @since 2.0.0001 (2008-01-07)

2861          */
2862  		public function GetNumChars($s) {
2863              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
2864                  return count($this->UTF8StringToArray($s));
2865              } 
2866              return strlen($s);
2867          }
2868              
2869          /**

2870          * Fill the list of available fonts ($this->fontlist).

2871          * @access protected

2872          * @since 4.0.013 (2008-07-28)

2873          */
2874  		protected function getFontsList() {
2875              $fontsdir = opendir($this->_getfontpath());
2876              while (($file = readdir($fontsdir)) !== false) {
2877                  if (substr($file, -4) == '.php') {
2878                      array_push($this->fontlist, strtolower(basename($file, '.php')));
2879                  }
2880              }
2881              closedir($fontsdir);
2882          }
2883          
2884          /**

2885          * Imports a TrueType, Type1, core, or CID0 font and makes it available.

2886          * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 

2887          * 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.

2888          * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.

2889          * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>

2890          * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.

2891          * @return array containing the font data, or false in case of error.

2892          * @access public

2893          * @since 1.5

2894          * @see SetFont()

2895          */
2896  		public function AddFont($family, $style='', $fontfile='') {
2897              if ($this->empty_string($family)) {
2898                  if (!$this->empty_string($this->FontFamily)) {
2899                      $family = $this->FontFamily;
2900                  } else {
2901                      $this->Error('Empty font family');
2902                  }
2903              }
2904              $family = strtolower($family);
2905              if ((!$this->isunicode) AND ($family == 'arial')) {
2906                  $family = 'helvetica';
2907              }
2908              if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
2909                  $style = '';
2910              }
2911              $tempstyle = strtoupper($style);
2912              $style = '';
2913              // underline

2914              if (strpos($tempstyle, 'U') !== false) {
2915                  $this->underline = true;
2916              } else {
2917                  $this->underline = false;
2918              }
2919              // line through (deleted)

2920              if (strpos($tempstyle, 'D') !== false) {
2921                  $this->linethrough = true;
2922              } else {
2923                  $this->linethrough = false;
2924              }
2925              // bold

2926              if (strpos($tempstyle, 'B') !== false) {
2927                  $style .= 'B';
2928              }
2929              // oblique

2930              if (strpos($tempstyle, 'I') !== false) {
2931                  $style .= 'I';
2932              }
2933              $bistyle = $style;
2934              $fontkey = $family.$style;
2935              $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
2936              $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
2937              // check if the font has been already added

2938              if ($this->getFontBuffer($fontkey) !== false) {
2939                  return $fontdata;
2940              }
2941              if (isset($type)) {
2942                  unset($type); 
2943              }
2944              if (isset($cw)) {
2945                  unset($cw); 
2946              }
2947              // get specified font directory (if any)

2948              $fontdir = '';
2949              if (!$this->empty_string($fontfile)) {
2950                  $fontdir = dirname($fontfile);
2951                  if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
2952                      $fontdir = '';
2953                  } else {
2954                      $fontdir .= '/';
2955                  }
2956              }
2957              // search and include font file

2958              if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
2959                  // build a standard filenames for specified font

2960                  $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
2961                  $fontfile2 = str_replace(' ', '', $family).'.php';
2962                  // search files on various directories

2963                  if (file_exists($fontdir.$fontfile1)) {
2964                      $fontfile = $fontdir.$fontfile1;
2965                  } elseif (file_exists($this->_getfontpath().$fontfile1)) {
2966                      $fontfile = $this->_getfontpath().$fontfile1;
2967                  } elseif (file_exists($fontfile1)) {
2968                      $fontfile = $fontfile1;
2969                  } elseif (file_exists($fontdir.$fontfile2)) {
2970                      $fontfile = $fontdir.$fontfile2;
2971                  } elseif (file_exists($this->_getfontpath().$fontfile2)) {
2972                      $fontfile = $this->_getfontpath().$fontfile2;
2973                  } else {
2974                      $fontfile = $fontfile2;
2975                  }
2976              }
2977              // include font file

2978              if (file_exists($fontfile)) {
2979                  include($fontfile);
2980              } else {
2981                  $this->Error('Could not include font definition file: '.$family.'');
2982              }
2983              // check font parameters

2984              if ((!isset($type)) OR (!isset($cw))) {
2985                  $this->Error('The font definition file has a bad format: '.$fontfile.'');
2986              }
2987              if (!isset($file)) {
2988                  $file = '';
2989              }
2990              if (!isset($enc)) {
2991                  $enc = '';
2992              }
2993              if (!isset($dw) OR $this->empty_string($dw)) {
2994                  // set default width

2995                  if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
2996                      $dw = $desc['MissingWidth'];
2997                  } elseif (isset($cw[32])) {
2998                      $dw = $cw[32];
2999                  } else {
3000                      $dw = 600;
3001                  }
3002              }
3003              ++$this->numfonts;            
3004              // register CID font (all styles at once)

3005              if ($type == 'cidfont0') {
3006                  $file = ''; // not embedded

3007                  $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
3008                  $sname = $name.$styles[$bistyle];
3009                  if ((strpos($bistyle, 'B') !== false) AND (isset($desc['StemV'])) AND ($desc['StemV'] == 70)) {
3010                      $desc['StemV'] = 120;
3011                  }
3012                  $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc));
3013              } elseif ($type == 'core') {
3014                  $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $dw));
3015              } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
3016                  $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'file' => $file, 'enc' => $enc, 'desc' => $desc));
3017              } elseif ($type == 'TrueTypeUnicode') {
3018                  $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg));
3019              } else {
3020                  $this->Error('Unknow font type: '.$type.'');
3021              }
3022              if (isset($diff) AND (!empty($diff))) {
3023                  //Search existing encodings

3024                  $d = 0;
3025                  $nb = count($this->diffs);
3026                  for ($i=1; $i <= $nb; ++$i) {
3027                      if ($this->diffs[$i] == $diff) {
3028                          $d = $i;
3029                          break;
3030                      }
3031                  }
3032                  if ($d == 0) {
3033                      $d = $nb + 1;
3034                      $this->diffs[$d] = $diff;
3035                  }
3036                  $this->setFontSubBuffer($fontkey, 'diff', $d);
3037              }
3038              if (!$this->empty_string($file)) {
3039                  if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
3040                      $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir);
3041                  } elseif ($type != 'core') {
3042                      $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir);
3043                  }
3044              }
3045              return $fontdata;
3046          }
3047  
3048          /**

3049          * Sets the font used to print character strings. 

3050          * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).

3051          * The method can be called before the first page is created and the font is retained from page to page. 

3052          * If you just wish to change the current font size, it is simpler to call SetFontSize().

3053          * 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 />

3054          * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.

3055          * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.

3056          * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12

3057          * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.

3058          * @access public

3059          * @since 1.0

3060          * @see AddFont(), SetFontSize()

3061          */
3062  		public function SetFont($family, $style='', $size=0, $fontfile='') {
3063              //Select a font; size given in points

3064              if ($size == 0) {
3065                  $size = $this->FontSizePt;
3066              }
3067              // try to add font (if not already added)

3068              $fontdata = $this->AddFont($family, $style, $fontfile);
3069              $this->FontFamily = $fontdata['family'];
3070              $this->FontStyle = $fontdata['style'];
3071              $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
3072              $this->SetFontSize($size);
3073          }
3074  
3075          /**

3076          * Defines the size of the current font.

3077          * @param float $size The size (in points)

3078          * @access public

3079          * @since 1.0

3080          * @see SetFont()

3081          */
3082  		public function SetFontSize($size) {
3083              //Set font size in points

3084              $this->FontSizePt = $size;
3085              $this->FontSize = $size / $this->k;
3086              if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
3087                  $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
3088              } else {
3089                  $this->FontAscent = 0.8 * $this->FontSize;
3090              }
3091              if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
3092                  $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
3093              } else {
3094                  $this->FontDescent = 0.2 * $this->FontSize;
3095              }
3096              if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
3097                  $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
3098              }
3099          }
3100  
3101          /**

3102          * Defines the default monospaced font.

3103          * @param string $font Font name.

3104          * @access public

3105          * @since 4.5.025

3106          */
3107  		public function SetDefaultMonospacedFont($font) {
3108              $this->default_monospaced_font = $font;
3109          }
3110          
3111          /**

3112          * 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 />

3113          * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().

3114          * @access public

3115          * @since 1.5

3116          * @see Cell(), Write(), Image(), Link(), SetLink()

3117          */
3118  		public function AddLink() {
3119              //Create a new internal link

3120              $n = count($this->links) + 1;
3121              $this->links[$n] = array(0, 0);
3122              return $n;
3123          }
3124  
3125          /**

3126          * Defines the page and position a link points to.

3127          * @param int $link The link identifier returned by AddLink()

3128          * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)

3129          * @param int $page Number of target page; -1 indicates the current page. This is the default value

3130          * @access public

3131          * @since 1.5

3132          * @see AddLink()

3133          */
3134  		public function SetLink($link, $y=0, $page=-1) {
3135              if ($y == -1) {
3136                  $y = $this->y;
3137              }
3138              if ($page == -1) {
3139                  $page = $this->page;
3140              }
3141              $this->links[$link] = array($page, $y);
3142          }
3143  
3144          /**

3145          * Puts a link on a rectangular area of the page.

3146          * 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.

3147          * @param float $x Abscissa of the upper-left corner of the rectangle

3148          * @param float $y Ordinate of the upper-left corner of the rectangle

3149          * @param float $w Width of the rectangle

3150          * @param float $h Height of the rectangle

3151          * @param mixed $link URL or identifier returned by AddLink()

3152          * @param int $spaces number of spaces on the text to link

3153          * @access public

3154          * @since 1.5

3155          * @see AddLink(), Annotation(), Cell(), Write(), Image()

3156          */
3157  		public function Link($x, $y, $w, $h, $link, $spaces=0) {
3158              $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
3159          }
3160          
3161          /**

3162          * Puts a markup annotation on a rectangular area of the page.

3163          * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!

3164          * @param float $x Abscissa of the upper-left corner of the rectangle

3165          * @param float $y Ordinate of the upper-left corner of the rectangle

3166          * @param float $w Width of the rectangle

3167          * @param float $h Height of the rectangle

3168          * @param string $text annotation text or alternate content

3169          * @param array $opt array of options (see section 8.4 of PDF reference 1.7).

3170          * @param int $spaces number of spaces on the text to link

3171          * @access public

3172          * @since 4.0.018 (2008-08-06)

3173          */
3174  		public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
3175              // recalculate coordinates to account for graphic transformations

3176              if (isset($this->transfmatrix)) {
3177                  $maxid = count($this->transfmatrix) - 1;
3178                  for ($i=$maxid; $i >= 0; $i--) {
3179                      $ctm = $this->transfmatrix[$i];
3180                      if (isset($ctm['a'])) {
3181                          $x = $x * $this->k;
3182                          $y = ($this->h - $y) * $this->k;
3183                          $w = $w * $this->k;
3184                          $h = $h * $this->k;
3185                          // top left

3186                          $xt = $x;
3187                          $yt = $y;
3188                          $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3189                          $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3190                          // top right

3191                          $xt = $x + $w;
3192                          $yt = $y;
3193                          $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3194                          $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3195                          // bottom left

3196                          $xt = $x;
3197                          $yt = $y - $h;
3198                          $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3199                          $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3200                          // bottom right

3201                          $xt = $x + $w;
3202                          $yt = $y - $h;
3203                          $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3204                          $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3205                          // new coordinates (rectangle area)

3206                          $x = min($x1, $x2, $x3, $x4);
3207                          $y = max($y1, $y2, $y3, $y4);
3208                          $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
3209                          $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
3210                          $x = $x / $this->k;
3211                          $y = $this->h - ($y / $this->k);
3212                      }
3213                  }
3214              }
3215              $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
3216              if (($opt['Subtype'] == 'FileAttachment') AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
3217                  $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + 100000));
3218              }
3219          }
3220  
3221          /**

3222          * Embedd the attached files.

3223          * @since 4.4.000 (2008-12-07)

3224          * @access protected

3225          * @see Annotation()

3226          */
3227  		protected function _putEmbeddedFiles() {
3228              reset($this->embeddedfiles);
3229              foreach ($this->embeddedfiles as $filename => $filedata) {
3230                  $data = file_get_contents($filedata['file']);
3231                  $filter = '';
3232                  if ($this->compress) {
3233                      $data = gzcompress($data);
3234                      $filter = ' /Filter /FlateDecode';
3235                  }
3236                  $this->offsets[$filedata['n']] = $this->bufferlen;
3237                  $this->_out($filedata['n'].' 0 obj');
3238                  $this->_out('<</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>');
3239                  $this->_putstream($data);
3240                  $this->_out('endobj');
3241              }
3242          }
3243          
3244          /**

3245          * Prints a character string.

3246          * The origin is on the left of the first charcter, on the baseline.

3247          * This method allows to place a string precisely on the page.

3248          * @param float $x Abscissa of the origin

3249          * @param float $y Ordinate of the origin

3250          * @param string $txt String to print

3251          * @param int $stroke outline size in points (0 = disable)

3252          * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).

3253          * @access public

3254          * @since 1.0

3255          * @deprecated deprecated since version 4.3.005 (2008-11-25)

3256          * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()

3257          */
3258  		public function Text($x, $y, $txt, $stroke=0, $clip=false) {
3259              //Output a string

3260              if ($this->rtl) {
3261                  // bidirectional algorithm (some chars may be changed affecting the line length)

3262                  $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
3263                  $l = $this->GetArrStringWidth($s);
3264                  $xr = $this->w - $x - $this->GetArrStringWidth($s);
3265              } else {
3266                  $xr = $x;
3267              }
3268              $opt = '';
3269              if (($stroke > 0) AND (!$clip)) {
3270                  $opt .= '1 Tr '.intval($stroke).' w ';
3271              } elseif (($stroke > 0) AND $clip) {
3272                  $opt .= '5 Tr '.intval($stroke).' w ';
3273              } elseif ($clip) {
3274                  $opt .= '7 Tr ';
3275              }
3276              $s = sprintf('BT %.2F %.2F Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
3277              if ($this->underline AND ($txt!='')) {
3278                  $s .= ' '.$this->_dounderline($xr, $y, $txt);
3279              }
3280              if ($this->linethrough AND ($txt!='')) { 
3281                  $s .= ' '.$this->_dolinethrough($xr, $y, $txt); 
3282              }
3283              if ($this->ColorFlag AND (!$clip)) {
3284                  $s='q '.$this->TextColor.' '.$s.' Q';
3285              }
3286              $this->_out($s);
3287          }
3288  
3289          /**

3290          * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 

3291          * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />

3292          * This method is called automatically and should not be called directly by the application.

3293          * @return boolean

3294          * @access public

3295          * @since 1.4

3296          * @see SetAutoPageBreak()

3297          */
3298  		public function AcceptPageBreak() {
3299              return $this->AutoPageBreak;
3300          }
3301          
3302          /**

3303          * Add page if needed.

3304          * @param float $h Cell height. Default value: 0.

3305          * @param mixed $y starting y position, leave empty for current position.

3306          * @return boolean true in case of page break, false otherwise.

3307          * @since 3.2.000 (2008-07-01)

3308          * @access protected

3309          */
3310  		protected function checkPageBreak($h=0, $y='') {
3311              if ($this->empty_string($y)) {
3312                  $y = $this->y;
3313              }
3314              if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
3315                  //Automatic page break

3316                  $x = $this->x;
3317                  $this->AddPage($this->CurOrientation);
3318                  $this->y = $this->tMargin;
3319                  $oldpage = $this->page - 1;
3320                  if ($this->rtl) {
3321                      if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
3322                          $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
3323                      } else {
3324                          $this->x = $x;
3325                      }
3326                  } else {
3327                      if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3328                          $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
3329                      } else {
3330                          $this->x = $x;
3331                      }
3332                  }
3333                  return true;
3334              }
3335              return false;
3336          }
3337  
3338          /**

3339          * 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 />

3340          * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.

3341          * @param float $w Cell width. If 0, the cell extends up to the right margin.

3342          * @param float $h Cell height. Default value: 0.

3343          * @param string $txt String to print. Default value: empty string.

3344          * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>

3345          * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>

3346          Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.

3347          * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>

3348          * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.

3349          * @param mixed $link URL or identifier returned by AddLink().

3350          * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>

3351          * @param boolean $ignore_min_height if true ignore automatic minimum height value.

3352          * @access public

3353          * @since 1.0

3354          * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()

3355          */
3356  		public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3357              //$min_cell_height = $this->FontAscent + $this->FontDescent;

3358              $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3359              if ($h < $min_cell_height) {
3360                  $h = $min_cell_height;
3361              }
3362              $this->checkPageBreak($h);
3363              $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height));
3364          }
3365  
3366          /**

3367          * Removes SHY characters from text.

3368          * @param string $txt input string

3369          * @return string without SHY characters.

3370          * @access public

3371          * @since (4.5.019) 2009-02-28

3372          */
3373  		public function removeSHY($txt='') {
3374              /*

3375              * Unicode Data

3376              * Name : SOFT HYPHEN, commonly abbreviated as SHY

3377              * HTML Entity (decimal): &#173;

3378              * HTML Entity (hex): &#xad;

3379              * HTML Entity (named): &shy;

3380              * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]

3381              * UTF-8 (hex): 0xC2 0xAD (c2ad)

3382              * UTF-8 character: chr(194).chr(173)

3383              */
3384              $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
3385              if (!$this->isunicode) {
3386                  $txt = preg_replace('/([\\xad]{1})/', '', $txt);
3387              }
3388              return $txt;
3389          }
3390          
3391          /**

3392          * 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 />

3393          * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.

3394          * @param float $w Cell width. If 0, the cell extends up to the right margin.

3395          * @param float $h Cell height. Default value: 0.

3396          * @param string $txt String to print. Default value: empty string.

3397          * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>

3398          * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.

3399          * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>

3400          * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.

3401          * @param mixed $link URL or identifier returned by AddLink().

3402          * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>

3403          * @param boolean $ignore_min_height if true ignore automatic minimum height value.

3404          * @access protected

3405          * @since 1.0

3406          * @see Cell()

3407          */
3408  		protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3409              $txt = $this->removeSHY($txt);
3410              $rs = ''; //string to be returned

3411              if (!$ignore_min_height) {
3412                  $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3413                  if ($h < $min_cell_height) {
3414                      $h = $min_cell_height;
3415                  }
3416              }
3417              $k = $this->k;
3418              if ($this->empty_string($w) OR ($w <= 0)) {
3419                  if ($this->rtl) {
3420                      $w = $this->x - $this->lMargin;
3421                  } else {
3422                      $w = $this->w - $this->rMargin - $this->x;
3423                  }
3424              }
3425              $s = '';            
3426              if (($fill == 1) OR ($border == 1)) {
3427                  if ($fill == 1) {
3428                      $op = ($border == 1) ? 'B' : 'f';
3429                  } else {
3430                      $op = 'S';
3431                  }
3432                  if ($this->rtl) {
3433                      $xk = (($this->x  - $w) * $k);
3434                  } else {
3435                      $xk = ($this->x * $k);
3436                  }
3437                  $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
3438              }
3439              if (is_string($border)) {
3440                  $lm = ($this->LineWidth / 2);
3441                  $x = $this->x;
3442                  $y = $this->y;
3443                  if (strpos($border,'L') !== false) {
3444                      if ($this->rtl) {
3445                          $xk = ($x - $w) * $k;
3446                      } else {
3447                          $xk = $x * $k;
3448                      }
3449                      $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k));
3450                  }
3451                  if (strpos($border,'T') !== false) {
3452                      if ($this->rtl) {
3453                          $xk = ($x - $w + $lm) * $k;
3454                          $xwk = ($x - $lm) * $k;
3455                      } else {
3456                          $xk = ($x - $lm) * $k;
3457                          $xwk = ($x + $w + $lm) * $k;
3458                      }
3459                      $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
3460                  }
3461                  if (strpos($border,'R') !== false) {
3462                      if ($this->rtl) {
3463                          $xk = $x * $k;
3464                      } else {
3465                          $xk = ($x + $w) * $k;
3466                      }
3467                      $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k));
3468                  }
3469                  if (strpos($border,'B') !== false) {
3470                      if ($this->rtl) {
3471                          $xk = ($x - $w + $lm) * $k;
3472                          $xwk = ($x - $lm) * $k;
3473                      } else {
3474                          $xk = ($x - $lm) * $k;
3475                          $xwk = ($x + $w + $lm) * $k;
3476                      }
3477                      $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
3478                  }
3479              }
3480              if ($txt != '') {
3481                  // text lenght

3482                  $width = $this->GetStringWidth($txt);
3483                  // ratio between cell lenght and text lenght

3484                  $ratio = ($w - (2 * $this->cMargin)) / $width;
3485                  
3486                  // stretch text if required

3487                  if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
3488                      if ($stretch > 2) {
3489                          // spacing

3490                          //Calculate character spacing in points

3491                          $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
3492                          //Set character spacing

3493                          $rs .= sprintf('BT %.2F Tc ET ', $char_space);
3494                      } else {
3495                          // scaling

3496                          //Calculate horizontal scaling

3497                          $horiz_scale = $ratio * 100.0;
3498                          //Set horizontal scaling

3499                          $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale);
3500                      }
3501                      $align = '';
3502                      $width = $w - (2 * $this->cMargin);
3503                  } else {
3504                      $stretch == 0;
3505                  }
3506                  if ($align == 'L') {
3507                      if ($this->rtl) {
3508                          $dx = $w - $width - $this->cMargin;
3509                      } else {
3510                          $dx = $this->cMargin;
3511                      }
3512                  } elseif ($align == 'R') {
3513                      if ($this->rtl) {
3514                          $dx = $this->cMargin;
3515                      } else {
3516                          $dx = $w - $width - $this->cMargin;
3517                      }
3518                  } elseif ($align == 'C') {
3519                      $dx = ($w - $width) / 2;
3520                  } elseif ($align == 'J') {
3521                      if ($this->rtl) {
3522                          $dx = $w - $width - $this->cMargin;
3523                      } else {
3524                          $dx = $this->cMargin;
3525                      }
3526                  } else {
3527                      $dx = $this->cMargin;
3528                  }
3529                  if ($this->ColorFlag) {
3530                      $s .= 'q '.$this->TextColor.' ';
3531                  }
3532                  $txt2 = $this->_escapetext($txt);
3533                  if ($this->rtl) {
3534                      $xdk = ($this->x - $dx - $width) * $k;
3535                  } else {
3536                      $xdk = ($this->x + $dx) * $k;
3537                  }
3538                  // Justification

3539                  if ($align == 'J') {
3540                      // count number of spaces

3541                      $ns = substr_count($txt, ' ');
3542                      if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
3543                          // get string width without spaces

3544                          $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3545                          // calculate average space width

3546                          $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3547                          // set word position to be used with TJ operator

3548                          $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3549                      } else {
3550                          // get string width

3551                          $width = $this->GetStringWidth($txt);
3552                          $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3553                          $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
3554                      }
3555                  }
3556                  // calculate approximate position of the font base line

3557                  //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);

3558                  $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3559                  // print text

3560                  $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3561                  if ($this->rtl) {
3562                      $xdx = $this->x - $dx - $width;
3563                  } else {
3564                      $xdx = $this->x + $dx;
3565                  }
3566                  if ($this->underline)  {
3567                      $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3568                  }
3569                  if ($this->linethrough) { 
3570                      $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3571                  }
3572                  if ($this->ColorFlag) {
3573                      $s .= ' Q';
3574                  }
3575                  if ($link) {
3576                      $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, substr_count($txt, chr(32)));
3577                  }
3578              }
3579              // output cell

3580              if ($s) {
3581                  // output cell

3582                  $rs .= $s;
3583                  // reset text stretching

3584                  if ($stretch > 2) {
3585                      //Reset character horizontal spacing

3586                      $rs .= ' BT 0 Tc ET';
3587                  } elseif ($stretch > 0) {
3588                      //Reset character horizontal scaling

3589                      $rs .= ' BT 100 Tz ET';
3590                  }
3591              }
3592              // reset word spacing

3593              if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) {
3594                  $rs .= ' BT 0 Tw ET';
3595              }
3596              $this->lasth = $h;
3597              if ($ln > 0) {
3598                  //Go to the beginning of the next line

3599                  $this->y += $h;
3600                  if ($ln == 1) {
3601                      if ($this->rtl) {
3602                          $this->x = $this->w - $this->rMargin;
3603                      } else {
3604                          $this->x = $this->lMargin;
3605                      }
3606                  }
3607              } else {
3608                  // go left or right by case

3609                  if ($this->rtl) {
3610                      $this->x -= $w;
3611                  } else {
3612                      $this->x += $w;
3613                  }
3614              }
3615              $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
3616              $rs = $gstyles.$rs;
3617              return $rs;
3618          }
3619  
3620          /**

3621          * This method allows printing text with line breaks. 

3622          * 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 />

3623          * Text can be aligned, centered or justified. The cell block can be framed and the background painted.

3624          * @param float $w Width of cells. If 0, they extend up to the right margin of the page.

3625          * @param float $h Cell minimum height. The cell extends automatically if needed.

3626          * @param string $txt String to print

3627          * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>

3628          * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul>

3629          * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.

3630          * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>

3631          * @param int $x x position in user units

3632          * @param int $y y position in user units

3633          * @param boolean $reseth if true reset the last cell height (default true).

3634          * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>

3635          * @param boolean $ishtml set to true if $txt is HTML content (default = false).

3636          * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.

3637          * @param float $maxh maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false.

3638          * @return int Return the number of cells or 1 for html mode.

3639          * @access public

3640          * @since 1.3

3641          * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()

3642          */
3643  		public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0) {    
3644              if ($this->empty_string($this->lasth) OR $reseth) {
3645                  //set row height

3646                  $this->lasth = $this->FontSize * $this->cell_height_ratio;
3647              }
3648              if (!$this->empty_string($y)) {
3649                  $this->SetY($y);
3650              } else {
3651                  $y = $this->GetY();
3652              }
3653              // check for page break

3654              $this->checkPageBreak($h);
3655              $y = $this->GetY();
3656              // get current page number

3657              $startpage = $this->page;
3658              if (!$this->empty_string($x)) {
3659                  $this->SetX($x);
3660              } else {
3661                  $x = $this->GetX();
3662              }
3663              if ($this->empty_string($w) OR ($w <= 0)) {
3664                  if ($this->rtl) {
3665                      $w = $this->x - $this->lMargin;
3666                  } else {
3667                      $w = $this->w - $this->rMargin - $this->x;
3668                  }
3669              }
3670              // store original margin values

3671              $lMargin = $this->lMargin;
3672              $rMargin = $this->rMargin;
3673              if ($this->rtl) {
3674                  $this->SetRightMargin($this->w - $this->x);
3675                  $this->SetLeftMargin($this->x - $w);
3676              } else {
3677                  $this->SetLeftMargin($this->x);
3678                  $this->SetRightMargin($this->w - $this->x - $w);
3679              }
3680              $starty = $this->y;
3681              if ($autopadding) {
3682                  // Adjust internal padding

3683                  if ($this->cMargin < ($this->LineWidth / 2)) {
3684                      $this->cMargin = ($this->LineWidth / 2);
3685                  }
3686                  // Add top space if needed

3687                  if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3688                      $this->y += $this->LineWidth / 2;
3689                  }
3690                  // add top padding

3691                  $this->y += $this->cMargin;
3692              }
3693              if ($ishtml) {
3694                  // ******* Write HTML text

3695                  $this->writeHTML($txt, true, 0, $reseth, true, $align);
3696                  $nl = 1;
3697              } else {
3698                  // ******* Write text

3699                  $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, false, $maxh);
3700              }
3701              if ($autopadding) {
3702                  // add bottom padding

3703                  $this->y += $this->cMargin;
3704                  // Add bottom space if needed

3705                  if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3706                      $this->y += $this->LineWidth / 2;
3707                  }
3708              }
3709              // Get end-of-text Y position

3710              $currentY = $this->y;
3711              // get latest page number

3712              $endpage = $this->page;
3713              // check if a new page has been created

3714              if ($endpage > $startpage) {
3715                  // design borders around HTML cells.

3716                  for ($page=$startpage; $page <= $endpage; ++$page) {
3717                      $this->setPage($page);
3718                      if ($page == $startpage) {
3719                          $this->y = $starty; // put cursor at the beginning of cell on the first page

3720                          $h = $this->getPageHeight() - $starty - $this->getBreakMargin();
3721                          $cborder = $this->getBorderMode($border, $position='start');
3722                      } elseif ($page == $endpage) {
3723                          $this->y = $this->tMargin; // put cursor at the beginning of last page

3724                          $h = $currentY - $this->tMargin;
3725                          $cborder = $this->getBorderMode($border, $position='end');
3726                      } else {
3727                          $this->y = $this->tMargin; // put cursor at the beginning of the current page

3728                          $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3729                          $cborder = $this->getBorderMode($border, $position='middle');
3730                      }
3731                      $nx = $x;
3732                      // account for margin changes

3733                      if ($page > $startpage) {
3734                          if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
3735                              $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
3736                          } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
3737                              $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
3738                          }
3739                      }
3740                      $this->SetX($nx);
3741                      $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false);
3742                      if ($cborder OR $fill) {
3743                          $pagebuff = $this->getPageBuffer($this->page);
3744                          $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
3745                          $pend = substr($pagebuff, $this->intmrk[$this->page]);
3746                          $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3747                          $this->intmrk[$this->page] += strlen($ccode."\n");
3748                      }
3749                  }
3750              } else {
3751                  $h = max($h, ($currentY - $y));
3752                  // put cursor at the beginning of text

3753                  $this->SetY($y); 
3754                  $this->SetX($x);
3755                  // design a cell around the text

3756                  $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true);
3757                  if ($border OR $fill) {
3758                      if (end($this->transfmrk[$this->page]) !== false) {
3759                          $pagemarkkey = key($this->transfmrk[$this->page]);
3760                          $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
3761                      } elseif ($this->InFooter) {
3762                          $pagemark = &$this->footerpos[$this->page];
3763                      } else {
3764                          $pagemark = &$this->intmrk[$this->page];
3765                      }
3766                      $pagebuff = $this->getPageBuffer($this->page);
3767                      $pstart = substr($pagebuff, 0, $pagemark);
3768                      $pend = substr($pagebuff, $pagemark);
3769                      $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3770                      $pagemark += strlen($ccode."\n");
3771                  }
3772              }
3773              // Get end-of-cell Y position

3774              $currentY = $this->GetY();
3775              // restore original margin values

3776              $this->SetLeftMargin($lMargin);
3777              $this->SetRightMargin($rMargin);
3778              if ($ln > 0) {
3779                  //Go to the beginning of the next line

3780                  $this->SetY($currentY);
3781                  if ($ln == 2) {
3782                      $this->SetX($x + $w);
3783                  }
3784              } else {
3785                  // go left or right by case

3786                  $this->setPage($startpage);
3787                  $this->y = $y;
3788                  $this->SetX($x + $w);
3789              }
3790              return $nl;
3791          }
3792  
3793          /**

3794          * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)

3795          * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>

3796          * @param string multicell position: 'start', 'middle', 'end'

3797          * @return border mode

3798          * @access protected

3799          * @since 4.4.002 (2008-12-09)

3800          */
3801  		protected function getBorderMode($border, $position='start') {
3802              if ((!$this->opencell) AND ($border == 1)) {
3803                  return 1;
3804              }
3805              $cborder = '';
3806              switch ($position) {
3807                  case 'start': {
3808                      if ($border == 1) {
3809                          $cborder = 'LTR';
3810                      } else {
3811                          if (!(false === strpos($border, 'L'))) {
3812                              $cborder .= 'L';
3813                          }
3814                          if (!(false === strpos($border, 'T'))) {
3815                              $cborder .= 'T';
3816                          }
3817                          if (!(false === strpos($border, 'R'))) {
3818                              $cborder .= 'R';
3819                          }
3820                          if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3821                              $cborder .= 'B';
3822                          }
3823                      }
3824                      break;
3825                  }
3826                  case 'middle': {
3827                      if ($border == 1) {
3828                          $cborder = 'LR';
3829                      } else {
3830                          if (!(false === strpos($border, 'L'))) {
3831                              $cborder .= 'L';
3832                          }
3833                          if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3834                              $cborder .= 'T';
3835                          }
3836                          if (!(false === strpos($border, 'R'))) {
3837                              $cborder .= 'R';
3838                          }
3839                          if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3840                              $cborder .= 'B';
3841                          }
3842                      }
3843                      break;
3844                  }
3845                  case 'end': {
3846                      if ($border == 1) {
3847                          $cborder = 'LRB';
3848                      } else {
3849                          if (!(false === strpos($border, 'L'))) {
3850                              $cborder .= 'L';
3851                          }
3852                          if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3853                              $cborder .= 'T';
3854                          }
3855                          if (!(false === strpos($border, 'R'))) {
3856                              $cborder .= 'R';
3857                          }
3858                          if (!(false === strpos($border, 'B'))) {
3859                              $cborder .= 'B';
3860                          }
3861                      }
3862                      break;
3863                  }
3864                  default: {
3865                      $cborder = $border;
3866                      break;
3867                  }
3868              }
3869              return $cborder;
3870          }
3871  
3872          /**

3873          * This method returns the estimated number of lines required to print the text.

3874          * @param string $txt text to print

3875          * @param float $w width of cell. If 0, they extend up to the right margin of the page.

3876          * @return int Return the estimated number of lines.

3877          * @access public

3878          * @since 4.5.011

3879          */
3880  		public function getNumLines($txt, $w=0) {
3881              $lines = 0;
3882              if ($this->empty_string($w) OR ($w <= 0)) {
3883                  if ($this->rtl) {
3884                      $w = $this->x - $this->lMargin;
3885                  } else {
3886                      $w = $this->w - $this->rMargin - $this->x;
3887                  }
3888              }
3889              // max column width

3890              $wmax = $w - (2 * $this->cMargin);
3891              // remove carriage returns

3892              $txt = str_replace("\r", '', $txt);
3893              // remove last newline (if any)

3894              if (substr($txt,-1) == "\n") {
3895                  $txt = substr($txt, 0, -1);
3896              }
3897              // divide text in blocks

3898              $txtblocks = explode("\n", $txt);
3899              // for each block;

3900              foreach ($txtblocks as $block) {
3901                  // estimate the number of lines

3902                  $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax));
3903              }
3904              return $lines;
3905          }
3906              
3907          /**

3908          * This method prints text from the current position.<br />

3909          * @param float $h Line height

3910          * @param string $txt String to print

3911          * @param mixed $link URL or identifier returned by AddLink()

3912          * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.

3913          * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>

3914          * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.

3915          * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>

3916          * @param boolean $firstline if true prints only the first line and return the remaining string.

3917          * @param boolean $firstblock if true the string is the starting of a line.

3918          * @param float $maxh maximum height. The remaining unprinted text will be returned. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.

3919          * @return mixed Return the number of cells or the remaining string if $firstline = true.

3920          * @access public

3921          * @since 1.5

3922          */
3923  		public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) {
3924              if (strlen($txt) == 0) {
3925                  $txt = ' ';
3926              }
3927              // remove carriage returns

3928              $s = str_replace("\r", '', $txt);
3929              // check if string contains arabic text

3930              if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3931                  $arabic = true;
3932              } else {
3933                  $arabic = false;
3934              }
3935              // check if string contains RTL text

3936              if ($arabic OR $this->tmprtl OR preg_match(K_RE_PATTERN_RTL, $txt)) {
3937                  $rtlmode = true;
3938              } else {
3939                  $rtlmode = false;
3940              }
3941              // get a char width

3942              $chrwidth = $this->GetCharWidth('.');
3943              // get array of unicode values

3944              $chars = $this->UTF8StringToArray($s);
3945              // get array of chars

3946              $uchars = $this->UTF8ArrayToUniArray($chars);
3947              // get the number of characters

3948              $nb = count($chars);
3949              // replacement for SHY character (minus symbol)

3950              $shy_replacement = 45;
3951              $shy_replacement_char = $this->unichr($shy_replacement);
3952              // widht for SHY replacement

3953              $shy_replacement_width = $this->GetCharWidth($shy_replacement);
3954              // store current position

3955              $prevx = $this->x;
3956              $prevy = $this->y;
3957              // max Y

3958              $maxy = $this->y + $maxh - $h - (2 * $this->cMargin);
3959              // calculate remaining line width ($w)

3960              if ($this->rtl) {
3961                  $w = $this->x - $this->lMargin;
3962              } else {
3963                  $w = $this->w - $this->rMargin - $this->x;
3964              }
3965              // max column width

3966              $wmax = $w - (2 * $this->cMargin);
3967              $i = 0; // character position

3968              $j = 0; // current starting position

3969              $sep = -1; // position of the last blank space

3970              $shy = false; // true if the last blank is a soft hypen (SHY)

3971              $l = 0; // current string lenght

3972              $nl = 0; //number of lines

3973              $linebreak = false;
3974              // for each character

3975              while ($i < $nb) {
3976                  if (($maxh > 0) AND ($this->y >= $maxy) ) {
3977                      $firstline = true;
3978                  }
3979                  //Get the current character

3980                  $c = $chars[$i];
3981                  if ($c == 10) { // 10 = "\n" = new line
3982                      //Explicit line break

3983                      if ($align == 'J') {
3984                          if ($this->rtl) {
3985                              $talign = 'R';
3986                          } else {
3987                              $talign = 'L';
3988                          }
3989                      } else {
3990                          $talign = $align;
3991                      }
3992                      $tmpstr = $this->UniArrSubString($uchars, $j, $i);
3993                      if ($firstline) {
3994                          $startx = $this->x;
3995                          $tmparr = array_slice($chars, $j, $i);
3996                          if ($rtlmode) {
3997                              $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
3998                          }
3999                          $linew = $this->GetArrStringWidth($tmparr);
4000                          unset($tmparr);
4001                          if ($this->rtl) {
4002                              $this->endlinex = $startx - $linew;
4003                          } else {
4004                              $this->endlinex = $startx + $linew;
4005                          }
4006                          $w = $linew;
4007                          $tmpcmargin = $this->cMargin;
4008                          if ($maxh == 0) {
4009                              $this->cMargin = 0;
4010                          }
4011                      }
4012                      $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
4013                      unset($tmpstr);
4014                      if ($firstline) {
4015                          $this->cMargin = $tmpcmargin;
4016                          return ($this->UniArrSubString($uchars, $i));
4017                      }
4018                      ++$nl;
4019                      $j = $i + 1;
4020                      $l = 0;
4021                      $sep = -1;
4022                      $shy = false;
4023                      // account for margin changes

4024                      if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4025                          // AcceptPageBreak() may be overriden on extended classed to include margin changes

4026                          $this->AcceptPageBreak();
4027                      }
4028                      $w = $this->getRemainingWidth();
4029                      $wmax = $w - (2 * $this->cMargin);
4030                  } else {
4031                      // 160 is the non-breaking space.

4032                      // 173 is SHY (Soft Hypen).

4033                      // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.

4034                      // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.

4035                      // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.

4036                      if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
4037                          // update last blank space position

4038                          $sep = $i;
4039                          // check if is a SHY

4040                          if ($c == 173) {
4041                              $shy = true;
4042                          } else {
4043                              $shy = false;
4044                          }
4045                      }
4046                      // update string length

4047                      if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) {
4048                          // with bidirectional algorithm some chars may be changed affecting the line length

4049                          // *** very slow ***

4050                          $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), '', $this->tmprtl));
4051                      } else {
4052                          $l += $this->GetCharWidth($c);
4053                      }
4054                      if (($l > $wmax) OR ($shy AND (($l + $shy_replacement_width) > $wmax)) ) {
4055                          // we have reached the end of column

4056                          if ($sep == -1) {
4057                              // check if the line was already started

4058                              if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
4059                                  OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
4060                                  // print a void cell and go to next line

4061                                  $this->Cell($w, $h, '', 0, 1);
4062                                  $linebreak = true;
4063                                  if ($firstline) {
4064                                      return ($this->UniArrSubString($uchars, $j));
4065                                  }
4066                              } else {
4067                                  // truncate the word because do not fit on column

4068                                  $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4069                                  if ($firstline) {
4070                                      $startx = $this->x;
4071                                      $tmparr = array_slice($chars, $j, $i);
4072                                      if ($rtlmode) {
4073                                          $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4074                                      }
4075                                      $linew = $this->GetArrStringWidth($tmparr);
4076                                      unset($tmparr);
4077                                      if ($this->rtl) {
4078                                          $this->endlinex = $startx - $linew;
4079                                      } else {
4080                                          $this->endlinex = $startx + $linew;
4081                                      }
4082                                      $w = $linew;
4083                                      $tmpcmargin = $this->cMargin;
4084                                      if ($maxh == 0) {
4085                                          $this->cMargin = 0;
4086                                      }
4087                                  }
4088                                  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
4089                                  unset($tmpstr);
4090                                  if ($firstline) {
4091                                      $this->cMargin = $tmpcmargin;
4092                                      return ($this->UniArrSubString($uchars, $i));
4093                                  }
4094                                  $j = $i;
4095                                  --$i;
4096                              }    
4097                          } else {
4098                              // word wrapping

4099                              if ($this->rtl AND (!$firstblock)) {
4100                                  $endspace = 1;
4101                              } else {
4102                                  $endspace = 0;
4103                              }
4104                              if ($shy) {
4105                                  // add hypen (minus symbol) at the end of the line

4106                                  $shy_width = $shy_replacement_width;
4107                                  if ($this->rtl) {
4108                                      $shy_char_left = $shy_replacement_char;
4109                                      $shy_char_right = '';
4110                                  } else {
4111                                      $shy_char_left = '';
4112                                      $shy_char_right = $shy_replacement_char;
4113                                  }
4114                              } else {
4115                                  $shy_width = 0;
4116                                  $shy_char_left = '';
4117                                  $shy_char_right = '';
4118                              }
4119                              $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
4120                              if ($firstline) {
4121                                  $startx = $this->x;
4122                                  $tmparr = array_slice($chars, $j, ($sep + $endspace));
4123                                  if ($rtlmode) {
4124                                      $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4125                                  }
4126                                  $linew = $this->GetArrStringWidth($tmparr);
4127                                  unset($tmparr);
4128                                  if ($this->rtl) {
4129                                      $this->endlinex = $startx - $linew - $shy_width;
4130                                  } else {
4131                                      $this->endlinex = $startx + $linew + $shy_width;
4132                                  }
4133                                  $w = $linew;
4134                                  $tmpcmargin = $this->cMargin;
4135                                  if ($maxh == 0) {
4136                                      $this->cMargin = 0;
4137                                  }
4138                              }
4139                              // print the line

4140                              $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
4141                              unset($tmpstr);
4142                              if ($firstline) {
4143                                  // return the remaining text

4144                                  $this->cMargin = $tmpcmargin;
4145                                  return ($this->UniArrSubString($uchars, ($sep + $endspace)));
4146                              }
4147                              $i = $sep;
4148                              $sep = -1;
4149                              $shy = false;
4150                              $j = ($i+1);
4151                          }
4152                          // account for margin changes

4153                          if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4154                              // AcceptPageBreak() may be overriden on extended classed to include margin changes

4155                              $this->AcceptPageBreak();
4156                          }
4157                          $w = $this->getRemainingWidth();
4158                          $wmax = $w - (2 * $this->cMargin);
4159                          if ($linebreak) {
4160                              $linebreak = false;
4161                          } else {
4162                              ++$nl;
4163                              $l = 0;
4164                          }
4165                      }
4166                  }
4167                  ++$i;
4168              } // end while i < nb

4169              // print last substring (if any)

4170              if ($l > 0) {
4171                  switch ($align) {
4172                      case 'J':
4173                      case 'C': {
4174                          $w = $w;
4175                          break;
4176                      }
4177                      case 'L': {
4178                          if ($this->rtl) {
4179                              $w = $w;
4180                          } else {
4181                              $w = $l;
4182                          }
4183                          break;
4184                      }
4185                      case 'R': {
4186                          if ($this->rtl) {
4187                              $w = $l;
4188                          } else {
4189                              $w = $w;
4190                          }
4191                          break;
4192                      }
4193                      default: {
4194                          $w = $l;
4195                          break;
4196                      }
4197                  }
4198                  $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
4199                  if ($firstline) {
4200                      $startx = $this->x;
4201                      $tmparr = array_slice($chars, $j, $nb);
4202                      if ($rtlmode) {
4203                          $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4204                      }
4205                      $linew = $this->GetArrStringWidth($tmparr);
4206                      unset($tmparr);
4207                      if ($this->rtl) {
4208                          $this->endlinex = $startx - $linew;
4209                      } else {
4210                          $this->endlinex = $startx + $linew;
4211                      }
4212                      $w = $linew;
4213                      $tmpcmargin = $this->cMargin;
4214                      if ($maxh == 0) {
4215                          $this->cMargin = 0;
4216                      }
4217                  }
4218                  $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
4219                  unset($tmpstr);
4220                  if ($firstline) {
4221                      $this->cMargin = $tmpcmargin;
4222                      return ($this->UniArrSubString($uchars, $nb));
4223                  }
4224                  ++$nl;
4225              }
4226              if ($firstline) {
4227                  return '';
4228              }
4229              return $nl;
4230          }
4231                  
4232          /**

4233          * Returns the remaining width between the current position and margins.

4234          * @return int Return the remaining width

4235          * @access protected

4236          */
4237  		protected function getRemainingWidth() {
4238              if ($this->rtl) {
4239                  return ($this->x - $this->lMargin);
4240              } else {
4241                  return ($this->w - $this->rMargin - $this->x);
4242              }
4243          }
4244  
4245           /**

4246          * Extract a slice of the $strarr array and return it as string.

4247          * @param string $strarr The input array of characters.

4248          * @param int $start the starting element of $strarr.

4249          * @param int $end first element that will not be returned.

4250          * @return Return part of a string

4251          * @access public

4252          */
4253  		public function UTF8ArrSubString($strarr, $start='', $end='') {
4254              if (strlen($start) == 0) {
4255                  $start = 0;
4256              }
4257              if (strlen($end) == 0) {
4258                  $end = count($strarr);
4259              }
4260              $string = '';
4261              for ($i=$start; $i < $end; ++$i) {
4262                  $string .= $this->unichr($strarr[$i]);
4263              }
4264              return $string;
4265          }
4266  
4267           /**

4268          * Extract a slice of the $uniarr array and return it as string.

4269          * @param string $uniarr The input array of characters.

4270          * @param int $start the starting element of $strarr.

4271          * @param int $end first element that will not be returned.

4272          * @return Return part of a string

4273          * @access public

4274          * @since 4.5.037 (2009-04-07)

4275          */
4276  		public function UniArrSubString($uniarr, $start='', $end='') {
4277              if (strlen($start) == 0) {
4278                  $start = 0;
4279              }
4280              if (strlen($end) == 0) {
4281                  $end = count($uniarr);
4282              }
4283              $string = '';
4284              for ($i=$start; $i < $end; ++$i) {
4285                  $string .= $uniarr[$i];
4286              }
4287              return $string;
4288          }
4289  
4290           /**

4291          * Convert an array of UTF8 values to array of unicode characters

4292          * @param string $ta The input array of UTF8 values.

4293          * @return Return array of unicode characters

4294          * @access public

4295          * @since 4.5.037 (2009-04-07)

4296          */
4297  		public function UTF8ArrayToUniArray($ta) {
4298              return array_map(array($this, 'unichr'), $ta);
4299          }
4300          
4301          /**

4302          * Returns the unicode caracter specified by UTF-8 code

4303          * @param int $c UTF-8 code

4304          * @return Returns the specified character.

4305          * @author Miguel Perez, Nicola Asuni

4306          * @access public

4307          * @since 2.3.000 (2008-03-05)

4308          */
4309  		public function unichr($c) {
4310              if (!$this->isunicode) {
4311                  return chr($c);
4312              } elseif ($c <= 0x7F) {
4313                  // one byte

4314                  return chr($c);
4315              } elseif ($c <= 0x7FF) {
4316                  // two bytes

4317                  return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
4318              } elseif ($c <= 0xFFFF) {
4319                  // three bytes

4320                  return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4321              } elseif ($c <= 0x10FFFF) {
4322                  // four bytes

4323                  return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4324              } else {
4325                  return '';
4326              }
4327          }
4328          
4329          /**

4330          * Puts an image in the page. 

4331          * The upper-left corner must be given. 

4332          * The dimensions can be specified in different ways:<ul>

4333          * <li>explicit width and height (expressed in user unit)</li>

4334          * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>

4335          * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>

4336          * 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;

4337          * The format can be specified explicitly or inferred from the file extension.<br />

4338          * It is possible to put a link on the image.<br />

4339          * Remark: if an image is used several times, only one copy will be embedded in the file.<br />

4340          * @param string $file Name of the file containing the image.

4341          * @param float $x Abscissa of the upper-left corner.

4342          * @param float $y Ordinate of the upper-left corner.

4343          * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.

4344          * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.

4345          * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.

4346          * @param mixed $link URL or identifier returned by AddLink().

4347          * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>

4348          * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).

4349          * @param int $dpi dot-per-inch resolution used on resize

4350          * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>

4351          * @param boolean $ismask true if this image is a mask, false otherwise

4352          * @param mixed $imgmask image object returned by this function or false

4353          * @param mixed $border Indicates if borders must be drawn around the image. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>

4354          * @param boolean $fitbox If true scale image dimensions proportionally to fit within the ($w, $h) box.

4355          * @return image information

4356          * @access public

4357          * @since 1.1

4358          */
4359  		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) {
4360              if ($x === '') {
4361                  $x = $this->x;
4362              }
4363              if ($y === '') {
4364                  $y = $this->y;
4365              }
4366              // get image dimensions

4367              $imsize = @getimagesize($file);
4368              if ($imsize === FALSE) {
4369                  // encode spaces on filename

4370                  $file = str_replace(' ', '%20', $file);
4371                  $imsize = @getimagesize($file);
4372                  if ($imsize === FALSE) {
4373                      $this->Error('[Image] No such file or directory in '.$file);
4374                  }
4375              }
4376              // get original image width and height in pixels

4377              list($pixw, $pixh) = $imsize;
4378              // calculate image width and height on document

4379              if (($w <= 0) AND ($h <= 0)) {
4380                  // convert image size to document unit

4381                  $w = $this->pixelsToUnits($pixw);
4382                  $h = $this->pixelsToUnits($pixh);
4383              } elseif ($w <= 0) {
4384                  $w = $h * $pixw / $pixh;
4385              } elseif ($h <= 0) {
4386                  $h = $w * $pixh / $pixw;
4387              } elseif ($fitbox AND ($w > 0) AND ($h > 0)) {
4388                  // scale image dimensions proportionally to fit within the ($w, $h) box

4389                  if ((($w * $pixh) / ($h * $pixw)) < 1) {
4390                      $h = $w * $pixh / $pixw;
4391                  } else {
4392                      $w = $h * $pixw / $pixh;
4393                  }
4394              }
4395              // calculate new minimum dimensions in pixels

4396              $neww = round($w * $this->k * $dpi / $this->dpi);
4397              $newh = round($h * $this->k * $dpi / $this->dpi);
4398              // check if resize is necessary (resize is used only to reduce the image)

4399              if (($neww * $newh) >= ($pixw * $pixh)) {
4400                  $resize = false;
4401              }
4402              // check if image has been already added on document

4403              if (!in_array($file, $this->imagekeys)) {
4404                  //First use of image, get info

4405                  if ($type == '') {
4406                      $fileinfo = pathinfo($file);
4407                      if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
4408                          $type = $fileinfo['extension'];
4409                      } else {
4410                          $this->Error('Image file has no extension and no type was specified: '.$file);
4411                      }
4412                  }
4413                  $type = strtolower($type);
4414                  if ($type == 'jpg') {
4415                      $type = 'jpeg';
4416                  }
4417                  $mqr = get_magic_quotes_runtime();
4418                  set_magic_quotes_runtime(0);
4419                  // Specific image handlers

4420                  $mtd = '_parse'.$type;
4421                  // GD image handler function

4422                  $gdfunction = 'imagecreatefrom'.$type;
4423                  $info = false;
4424                  if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
4425                      // TCPDF image functions

4426                      $info = $this->$mtd($file);
4427                      if ($info == 'pngalpha') {
4428                          return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
4429                      }
4430                  } 
4431                  if (!$info) {
4432                      if (function_exists($gdfunction)) {
4433                          // GD library

4434                          $img = $gdfunction($file);
4435                          if ($resize) {
4436                              $imgr = imagecreatetruecolor($neww, $newh);
4437                              imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 
4438                              $info = $this->_toJPEG($imgr);
4439                          } else {
4440                              $info = $this->_toJPEG($img);
4441                          }
4442                      } elseif (extension_loaded('imagick')) {
4443                          // ImageMagick library

4444                          $img = new Imagick();
4445                          $img->readImage($file);
4446                          if ($resize) {
4447                              $img->resizeImage($neww, $newh, 10, 1, false);
4448                          }
4449                          $img->setCompressionQuality($this->jpeg_quality);
4450                          $img->setImageFormat('jpeg');
4451                          $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4452                          $img->writeImage($tempname);
4453                          $info = $this->_parsejpeg($tempname);
4454                          unlink($tempname);
4455                          $img->destroy();
4456                      } else {
4457                          return;
4458                      }
4459                  }
4460                  if ($info === false) {
4461                      //If false, we cannot process image

4462                      return;
4463                  }
4464                  set_magic_quotes_runtime($mqr);
4465                  if ($ismask) {
4466                      // force grayscale

4467                      $info['cs'] = 'DeviceGray';
4468                  }
4469                  $info['i'] = $this->numimages + 1;
4470                  if ($imgmask !== false) {
4471                      $info['masked'] = $imgmask;
4472                  }
4473                  // add image to document

4474                  $this->setImageBuffer($file, $info);
4475              } else {
4476                  $info = $this->getImageBuffer($file);
4477              }
4478              // Check whether we need a new page first as this does not fit

4479              if ($this->checkPageBreak($h, $y)) {
4480                  $y = $this->GetY() + $this->cMargin;
4481              }
4482              // set bottomcoordinates

4483              $this->img_rb_y = $y + $h;
4484              // set alignment

4485              if ($this->rtl) {
4486                  if ($palign == 'L') {
4487                      $ximg = $this->lMargin;
4488                      // set right side coordinate

4489                      $this->img_rb_x = $ximg + $w;
4490                  } elseif ($palign == 'C') {
4491                      $ximg = ($this->w - $x - $w) / 2;
4492                      // set right side coordinate

4493                      $this->img_rb_x = $ximg + $w;
4494                  } else {
4495                      $ximg = $this->w - $x - $w;
4496                      // set left side coordinate

4497                      $this->img_rb_x = $ximg;
4498                  }
4499              } else {
4500                  if ($palign == 'R') {
4501                      $ximg = $this->w - $this->rMargin - $w;
4502                      // set left side coordinate

4503                      $this->img_rb_x = $ximg;
4504                  } elseif ($palign == 'C') {
4505                      $ximg = ($this->w - $x - $w) / 2;
4506                      // set right side coordinate

4507                      $this->img_rb_x = $ximg + $w;
4508                  } else {
4509                      $ximg = $x;
4510                      // set right side coordinate

4511                      $this->img_rb_x = $ximg + $w;
4512                  }
4513              }
4514              if ($ismask) {
4515                  // embed hidden, ouside the canvas

4516                  $xkimg = ($this->pagedim[$this->page]['w'] + 10);
4517              } else {
4518                  $xkimg = $ximg * $this->k;
4519              }
4520              $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
4521              if (!empty($border)) {
4522                  $bx = $x;
4523                  $by = $y;
4524                  $this->x = $ximg;
4525                  $this->y = $y;
4526                  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
4527                  $this->x = $bx;
4528                  $this->y = $by;
4529              }
4530              if ($link) {
4531                  $this->Link($ximg, $y, $w, $h, $link, 0);
4532              }
4533              // set pointer to align the successive text/objects

4534              switch($align) {
4535                  case 'T': {
4536                      $this->y = $y;
4537                      $this->x = $this->img_rb_x;
4538                      break;
4539                  }
4540                  case 'M': {
4541                      $this->y = $y + round($h/2);
4542                      $this->x = $this->img_rb_x;
4543                      break;
4544                  }
4545                  case 'B': {
4546                      $this->y = $this->img_rb_y;
4547                      $this->x = $this->img_rb_x;
4548                      break;
4549                  }
4550                  case 'N': {
4551                      $this->SetY($this->img_rb_y);
4552                      break;
4553                  }
4554                  default:{
4555                      break;
4556                  }
4557              }
4558              $this->endlinex = $this->img_rb_x;
4559              return $info['i'];
4560          }
4561                  
4562          /**

4563          * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.

4564          * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.

4565          * @param string $file Image file name.

4566          * @param image $image Image object.

4567          * return image JPEG image object.

4568          * @access protected

4569          */
4570  		protected function _toJPEG($image) {
4571              $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4572              imagejpeg($image, $tempname, $this->jpeg_quality);
4573              imagedestroy($image);
4574              $retvars = $this->_parsejpeg($tempname);
4575              // tidy up by removing temporary image

4576              unlink($tempname);
4577              return $retvars;
4578          }
4579          
4580          /**

4581          * Extract info from a JPEG file without using the GD library.

4582          * @param string $file image file to parse

4583          * @return array structure containing the image data

4584          * @access protected

4585          */
4586  		protected function _parsejpeg($file) {
4587              $a = getimagesize($file);
4588              if (empty($a)) {
4589                  $this->Error('Missing or incorrect image file: '.$file);
4590              }
4591              if ($a[2] != 2) {
4592                  $this->Error('Not a JPEG file: '.$file);
4593              }
4594              if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
4595                  $colspace = 'DeviceRGB';
4596              } elseif ($a['channels'] == 4) {
4597                  $colspace = 'DeviceCMYK';
4598              } else {
4599                  $colspace = 'DeviceGray';
4600              }
4601              $bpc = isset($a['bits']) ? $a['bits'] : 8;
4602              $data = file_get_contents($file);
4603              return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
4604          }
4605  
4606          /**

4607          * Extract info from a PNG file without using the GD library.

4608          * @param string $file image file to parse

4609          * @return array structure containing the image data

4610          * @access protected

4611          */
4612  		protected function _parsepng($file) {
4613              $f = fopen($file, 'rb');
4614              if ($f === false) {
4615                  $this->Error('Can\'t open image file: '.$file);
4616              }
4617              //Check signature

4618              if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
4619                  $this->Error('Not a PNG file: '.$file);
4620              }
4621              //Read header chunk

4622              fread($f, 4);
4623              if (fread($f, 4) != 'IHDR') {
4624                  $this->Error('Incorrect PNG file: '.$file);
4625              }
4626              $w = $this->_freadint($f);
4627              $h = $this->_freadint($f);
4628              $bpc = ord(fread($f, 1));
4629              if ($bpc > 8) {
4630                  //$this->Error('16-bit depth not supported: '.$file);

4631                  fclose($f);
4632                  return false;
4633              }
4634              $ct = ord(fread($f, 1));
4635              if ($ct == 0) {
4636                  $colspace = 'DeviceGray';
4637              } elseif ($ct == 2) {
4638                  $colspace = 'DeviceRGB';
4639              } elseif ($ct == 3) {
4640                  $colspace = 'Indexed';
4641              } else {
4642                  // alpha channel

4643                  fclose($f);
4644                  return 'pngalpha';
4645              }
4646              if (ord(fread($f, 1)) != 0) {
4647                  //$this->Error('Unknown compression method: '.$file);

4648                  fclose($f);
4649                  return false;
4650              }
4651              if (ord(fread($f, 1)) != 0) {
4652                  //$this->Error('Unknown filter method: '.$file);

4653                  fclose($f);
4654                  return false;
4655              }
4656              if (ord(fread($f, 1)) != 0) {
4657                  //$this->Error('Interlacing not supported: '.$file);

4658                  fclose($f);
4659                  return false;
4660              }
4661              fread($f, 4);
4662              $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
4663              //Scan chunks looking for palette, transparency and image data

4664              $pal = '';
4665              $trns = '';
4666              $data = '';
4667              do {
4668                  $n = $this->_freadint($f);
4669                  $type = fread($f, 4);
4670                  if ($type == 'PLTE') {
4671                      //Read palette

4672                      $pal = $this->rfread($f, $n);
4673                      fread($f, 4);
4674                  } elseif ($type == 'tRNS') {
4675                      //Read transparency info

4676                      $t = $this->rfread($f, $n);
4677                      if ($ct == 0) {
4678                          $trns = array(ord(substr($t, 1, 1)));
4679                      } elseif ($ct == 2) {
4680                          $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
4681                      } else {
4682                          $pos = strpos($t, chr(0));
4683                          if ($pos !== false) {
4684                              $trns = array($pos);
4685                          }
4686                      }
4687                      fread($f, 4);
4688                  } elseif ($type == 'IDAT') {
4689                      //Read image data block

4690                      $data .= $this->rfread($f, $n);
4691                      fread($f, 4);
4692                  } elseif ($type == 'IEND') {
4693                      break;
4694                  } else {
4695                      $this->rfread($f, $n + 4);
4696                  }
4697              } while ($n);
4698              if (($colspace == 'Indexed') AND (empty($pal))) {
4699                  //$this->Error('Missing palette in '.$file);

4700                  fclose($f);
4701                  return false;
4702              }
4703              fclose($f);
4704              return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
4705          }
4706  
4707          /**

4708          * Binary-safe and URL-safe file read.

4709          * Reads up to length  bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached.

4710          * @param resource $handle

4711          * @param int $length

4712          * @return Returns the read string or FALSE in case of error.

4713          * @author Nicola Asuni

4714          * @access protected

4715          * @since 4.5.027 (2009-03-16)

4716          */
4717  		protected function rfread($handle, $length) {
4718              $data = fread($handle, $length);
4719              if ($data === false) {
4720                  return false;
4721              }
4722              $rest = $length - strlen($data);
4723              if ($rest > 0) {
4724                  $data .= $this->rfread($handle, $rest);
4725              }
4726              return $data;
4727          }
4728  
4729          /**

4730          * Extract info from a PNG image with alpha channel using the GD library.

4731          * @param string $file Name of the file containing the image.

4732          * @param float $x Abscissa of the upper-left corner.

4733          * @param float $y Ordinate of the upper-left corner.

4734          * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.

4735          * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.

4736          * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.

4737          * @param mixed $link URL or identifier returned by AddLink().

4738          * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>

4739          * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).

4740          * @param int $dpi dot-per-inch resolution used on resize

4741          * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>

4742          * @author Valentin Schmidt, Nicola Asuni

4743          * @access protected

4744          * @since 4.3.007 (2008-12-04)

4745          * @see Image()

4746          */
4747  		protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
4748              // get image size

4749              list($wpx, $hpx) = getimagesize($file);
4750              // generate images

4751              $img = imagecreatefrompng($file);
4752              $imgalpha = imagecreate($wpx, $hpx);
4753              // generate gray scale pallete

4754              for ($c = 0; $c < 256; ++$c) {
4755                  ImageColorAllocate($imgalpha, $c, $c, $c);
4756              }
4757              // extract alpha channel

4758              for ($xpx = 0; $xpx < $wpx; ++$xpx) {
4759                  for ($ypx = 0; $ypx < $hpx; ++$ypx) {
4760                      $colorindex = imagecolorat($img, $xpx, $ypx);
4761                      $col = imagecolorsforindex($img, $colorindex);
4762                      imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127));
4763                  }
4764              }
4765              // create temp alpha file

4766              $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
4767              imagepng($imgalpha, $tempfile_alpha);
4768              imagedestroy($imgalpha);
4769              // extract image without alpha channel

4770              $imgplain = imagecreatetruecolor($wpx, $hpx);
4771              imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
4772              // create temp image file

4773              $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
4774              imagepng($imgplain, $tempfile_plain);
4775              imagedestroy($imgplain);
4776              // embed mask image

4777              $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
4778              // embed image, masked with previously embedded mask

4779              $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
4780              // remove temp files

4781              unlink($tempfile_alpha);
4782              unlink($tempfile_plain);
4783          }
4784  
4785          /**

4786          * Correct the gamma value to be used with GD library

4787          * @param float $v the gamma value to be corrected

4788          * @access protected

4789          * @since 4.3.007 (2008-12-04)

4790          */
4791  		protected function getGDgamma($v) {
4792              return (pow(($v / 255), 2.2) * 255);
4793          } 
4794          
4795          /**

4796          * Performs a line break. 

4797          * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.

4798          * @param float $h The height of the break. By default, the value equals the height of the last printed cell.

4799          * @param boolean $cell if true add a cMargin to the x coordinate

4800          * @access public

4801          * @since 1.0

4802          * @see Cell()

4803          */
4804          public function Ln($h='', $cell=false) {
4805              //Line feed; default value is last cell height

4806              if ($cell) {
4807                  $cellmargin = $this->cMargin;
4808              } else {
4809                  $cellmargin = 0;
4810              }
4811              if ($this->rtl) {
4812                  $this->x = $this->w - $this->rMargin - $cellmargin;
4813              } else {
4814                  $this->x = $this->lMargin + $cellmargin;
4815              }
4816              if (is_string($h)) {
4817                  $this->y += $this->lasth;
4818              } else {
4819                  $this->y += $h;
4820              }
4821              $this->newline = true;
4822          }
4823  
4824          /**

4825          * Returns the relative X value of current position.

4826          * The value is relative to the left border for LTR languages and to the right border for RTL languages.

4827          * @return float

4828          * @access public

4829          * @since 1.2

4830          * @see SetX(), GetY(), SetY()

4831          */
4832  		public function GetX() {
4833              //Get x position

4834              if ($this->rtl) {
4835                  return ($this->w - $this->x);
4836              } else {
4837                  return $this->x;
4838              }
4839          }
4840          
4841          /**

4842          * Returns the absolute X value of current position.

4843          * @return float

4844          * @access public

4845          * @since 1.2

4846          * @see SetX(), GetY(), SetY()

4847          */
4848  		public function GetAbsX() {
4849              return $this->x;
4850          }
4851          
4852          /**

4853          * Returns the ordinate of the current position.

4854          * @return float

4855          * @access public

4856          * @since 1.0

4857          * @see SetY(), GetX(), SetX()

4858          */
4859  		public function GetY() {
4860              //Get y position

4861              return $this->y;
4862          }
4863          
4864          /**

4865          * Defines the abscissa of the current position. 

4866          * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).

4867          * @param float $x The value of the abscissa.

4868          * @access public

4869          * @since 1.2

4870          * @see GetX(), GetY(), SetY(), SetXY()

4871          */
4872  		public function SetX($x) {
4873              //Set x position

4874              if ($this->rtl) {
4875                  if ($x >= 0) {
4876                      $this->x = $this->w - $x;
4877                  } else {
4878                      $this->x = abs($x);
4879                  }
4880              } else {
4881                  if ($x >= 0) {
4882                      $this->x = $x;
4883                  } else {
4884                      $this->x = $this->w + $x;
4885                  }
4886              }
4887              if ($this->x < 0) {
4888                  $this->x = 0;
4889              }
4890              if ($this->x > $this->w) {
4891                  $this->x = $this->w;
4892              }
4893          }
4894          
4895          /**

4896          * Moves the current abscissa back to the left margin and sets the ordinate.

4897          * If the passed value is negative, it is relative to the bottom of the page.

4898          * @param float $y The value of the ordinate.

4899          * @param bool $resetx if true (default) reset the X position.

4900          * @access public

4901          * @since 1.0

4902          * @see GetX(), GetY(), SetY(), SetXY()

4903          */
4904  		public function SetY($y, $resetx=true) {
4905              if ($resetx) {
4906                  //reset x

4907                  if ($this->rtl) {
4908                      $this->x = $this->w - $this->rMargin;
4909                  } else {
4910                      $this->x = $this->lMargin;
4911                  }
4912              }
4913              if ($y >= 0) {
4914                  $this->y = $y;
4915              } else {
4916                  $this->y = $this->h + $y;
4917              }
4918              if ($this->y < 0) {
4919                  $this->y = 0;
4920              }
4921              if ($this->y > $this->h) {
4922                  $this->y = $this->h;
4923              }
4924          }
4925          
4926          /**

4927          * Defines the abscissa and ordinate of the current position. 

4928          * If the passed values are negative, they are relative respectively to the right and bottom of the page.

4929          * @param float $x The value of the abscissa

4930          * @param float $y The value of the ordinate

4931          * @access public

4932          * @since 1.2

4933          * @see SetX(), SetY()

4934          */
4935  		public function SetXY($x, $y) {
4936              //Set x and y positions

4937              $this->SetY($y);
4938              $this->SetX($x);
4939          }
4940  
4941          /**

4942          * Send the document to a given destination: string, local file or browser. 

4943          * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />

4944          * The method first calls Close() if necessary to terminate the document.

4945          * @param string $name The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.

4946          * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul>

4947          * @access public

4948          * @since 1.0

4949          * @see Close()

4950          */
4951  		public function Output($name='doc.pdf', $dest='I') {
4952              //Output PDF to some destination

4953              //Finish document if necessary

4954              if ($this->state < 3) {
4955                  $this->Close();
4956              }
4957              //Normalize parameters

4958              if (is_bool($dest)) {
4959                  $dest = $dest ? 'D' : 'F';
4960              }
4961              $dest = strtoupper($dest);
4962              if ($dest != 'F') {
4963                  $name = preg_replace('/[\s]+/', '_', $name);
4964                  $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
4965              }
4966              if ($this->sign) {
4967                  // *** apply digital signature to the document ***

4968                  // get the document content

4969                  $pdfdoc = $this->getBuffer();
4970                  // remove last newline

4971                  $pdfdoc = substr($pdfdoc, 0, -1);
4972                  // Remove the original buffer

4973                  if (isset($this->diskcache) AND $this->diskcache) {
4974                      // remove buffer file from cache

4975                      unlink($this->buffer);
4976                  }
4977                  unset($this->buffer);
4978                  // remove filler space

4979                  $tmppos = strpos($pdfdoc, '/ByteRange[0 ********** ********** **********]') + 58;
4980                  $pdfdoc = substr($pdfdoc, 0, $tmppos).substr($pdfdoc, $tmppos + $this->signature_max_lenght);
4981                  // define the ByteRange

4982                  $byte_range = array();
4983                  $byte_range[0] = 0;
4984                  $byte_range[1] = $tmppos - 1;
4985                  $byte_range[2] = $byte_range[1] + $this->signature_max_lenght;
4986                  $byte_range[3] = strlen($pdfdoc) - $byte_range[1];
4987                  // replace the ByteRange

4988                  $byterange = sprintf('/ByteRange[0 %010u %010u %010u]', $byte_range[1], $byte_range[2], $byte_range[3]);
4989                  $pdfdoc = str_replace('/ByteRange[0 ********** ********** **********]', $byterange, $pdfdoc);
4990                  // write the document to a temporary folder

4991                  $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
4992                  $f = fopen($tempdoc, 'wb');
4993                  if (!$f) {
4994                      $this->Error('Unable to create temporary file: '.$tempdoc);
4995                  }
4996                  $pdfdoc_lenght = strlen($pdfdoc);
4997                  fwrite($f, $pdfdoc, $pdfdoc_lenght);
4998                  fclose($f);
4999                  // get digital signature.

5000                  // IS THE FOLLOWING PROCEDURE CORRECT? THE SIGNED DOCUMENTS ARE NOT VALID!

5001                  $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
5002                  if (empty($this->signature_data['extracerts'])) {
5003                      openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
5004                  } else {
5005                      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']);
5006                  }    
5007                  unlink($tempdoc);
5008                  // read signature

5009                  $signature = file_get_contents($tempsign, false, null, $pdfdoc_lenght);
5010                  unlink($tempsign);
5011                  // extract signature

5012                  $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
5013                  $tmparr = explode("\n\n", $signature);
5014                  $signature = $tmparr[1];
5015                  unset($tmparr);
5016                  // decode signature

5017                  $signature = base64_decode(trim($signature));
5018                  // convert signature to hex

5019                  $signature = current(unpack('H*', $signature));
5020                  $signature = str_pad($signature, $this->signature_max_lenght, '0');
5021                  // Add signature to the document

5022                  $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).$signature.substr($pdfdoc, (0 - $byte_range[3]));
5023                  $this->diskcache = false;
5024                  $this->buffer = &$pdfdoc;
5025                  $this->bufferlen = strlen($pdfdoc);
5026              }
5027              switch($dest) {
5028                  case 'I': {
5029                      // Send PDF to the standard output

5030                      if (ob_get_contents()) {
5031                          $this->Error('Some data has already been output, can\'t send PDF file');
5032                      }
5033                      if (php_sapi_name() != 'cli') {
5034                          //We send to a browser

5035                          header('Content-Type: application/pdf');
5036                          if (headers_sent()) {
5037                              $this->Error('Some data has already been output to browser, can\'t send PDF file');
5038                          }
5039                          header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1

5040                          header('Pragma: public');
5041                          header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past

5042                          header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');    
5043                          header('Content-Length: '.$this->bufferlen);
5044                          header('Content-Disposition: inline; filename="'.basename($name).'";');
5045                      }
5046                      echo $this->getBuffer();
5047                      break;
5048                  }
5049                  case 'D': {
5050                      // Download PDF as file

5051                      if (ob_get_contents()) {
5052                          $this->Error('Some data has already been output, can\'t send PDF file');
5053                      }
5054                      header('Content-Description: File Transfer');
5055                      if (headers_sent()) {
5056                          $this->Error('Some data has already been output to browser, can\'t send PDF file');
5057                      }
5058                      header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1

5059                      header('Pragma: public');
5060                      header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past

5061                      header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
5062                      // force download dialog

5063                      header('Content-Type: application/force-download');
5064                      //crm-now: changed to avoid error 500 with fastcgi

5065                      //header('Content-Type: application/octet-stream', false);

5066                      //header('Content-Type: application/download', false);

5067                      //header('Content-Type: application/pdf', false);

5068                      // use the Content-Disposition header to supply a recommended filename

5069                      header('Content-Disposition: attachment; filename="'.basename($name).'";');
5070                      header('Content-Transfer-Encoding: binary');
5071                      header('Content-Length: '.$this->bufferlen);
5072                      echo $this->getBuffer();
5073                      break;
5074                  }
5075                  case 'F': {
5076                      // Save PDF to a local file

5077                      if ($this->diskcache) {
5078                          copy($this->buffer, $name);
5079                      } else {
5080                          $f = fopen($name, 'wb');
5081                          if (!$f) {
5082                              $this->Error('Unable to create output file: '.$name);
5083                          }
5084                          fwrite($f, $this->getBuffer(), $this->bufferlen);
5085                          fclose($f);
5086                      }
5087                      break;
5088                  }
5089                  case 'S': {
5090                      // Returns PDF as a string

5091                      return $this->getBuffer();
5092                  }
5093                  default: {
5094                      $this->Error('Incorrect output destination: '.$dest);
5095                  }
5096              }
5097              return '';
5098          }
5099  
5100          /**

5101           * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache.

5102           * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables.

5103           * @param boolean $preserve_objcopy if true preserves the objcopy variable

5104           * @access public

5105           * @since 4.5.016 (2009-02-24)

5106           */
5107  		public function _destroy($destroyall=false, $preserve_objcopy=false) {
5108              if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
5109                  // remove buffer file from cache

5110                  unlink($this->buffer);
5111              }
5112              foreach (array_keys(get_object_vars($this)) as $val) {
5113                  if ($destroyall OR (
5114                      ($val != 'internal_encoding') 
5115                      AND ($val != 'state') 
5116                      AND ($val != 'bufferlen') 
5117                      AND ($val != 'buffer') 
5118                      AND ($val != 'diskcache')
5119                      AND ($val != 'sign')
5120                      AND ($val != 'signature_data')
5121                      AND ($val != 'signature_max_lenght')
5122                      )) {
5123                      if (!$preserve_objcopy OR ($val != 'objcopy')) {
5124                          unset($this->$val);
5125                      }
5126                  }
5127              }
5128          }
5129          
5130          /**

5131          * Check for locale-related bug

5132          * @access protected

5133          */
5134  		protected function _dochecks() {
5135              //Check for locale-related bug

5136              if (1.1 == 1) {
5137                  $this->Error('Don\'t alter the locale before including class file');
5138              }
5139              //Check for decimal separator

5140              if (sprintf('%.1F', 1.0) != '1.0') {
5141                  setlocale(LC_NUMERIC, 'C');
5142              }
5143          }
5144  
5145          /**

5146          * Return fonts path

5147          * @return string

5148          * @access protected

5149          */
5150  		protected function _getfontpath() {
5151              if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
5152                  define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
5153              }
5154              return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
5155          }
5156          
5157          /**

5158          * Output pages.

5159          * @access protected

5160          */
5161  		protected function _putpages() {
5162              $nb = $this->numpages;
5163              if (!empty($this->AliasNbPages)) {
5164                  $nbs = $this->formatPageNumber($nb);
5165                  $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font

5166                  $alias_a = $this->_escape($this->AliasNbPages);
5167                  $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
5168                  if ($this->isunicode) {
5169                      $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
5170                      $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
5171                      $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
5172                      $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
5173                  }
5174              }
5175              if (!empty($this->AliasNumPage)) {
5176                  $alias_pa = $this->_escape($this->AliasNumPage);
5177                  $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
5178                  if ($this->isunicode) {
5179                      $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
5180                      $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
5181                      $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
5182                      $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
5183                  }
5184              }
5185              $pagegroupnum = 0;
5186              $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
5187              for ($n=1; $n <= $nb; ++$n) {
5188                  $temppage = $this->getPageBuffer($n);
5189                  if (!empty($this->pagegroups)) {
5190                      if(isset($this->newpagegroup[$n])) {
5191                          $pagegroupnum = 0;
5192                      }
5193                      ++$pagegroupnum;
5194                      foreach ($this->pagegroups as $k => $v) {
5195                          // replace total pages group numbers

5196                          $vs = $this->formatPageNumber($v);
5197                          $vu = $this->UTF8ToUTF16BE($vs, false);
5198                          $alias_ga = $this->_escape($k);
5199                          $alias_gau = $this->_escape('{'.$k.'}');
5200                          if ($this->isunicode) {
5201                              $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
5202                              $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
5203                              $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
5204                              $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
5205                          }
5206                          $temppage = str_replace($alias_gau, $vu, $temppage);
5207                          if ($this->isunicode) {
5208                              $temppage = str_replace($alias_gbu, $vu, $temppage);
5209                              $temppage = str_replace($alias_gcu, $vu, $temppage);
5210                              $temppage = str_replace($alias_gb, $vs, $temppage);
5211                              $temppage = str_replace($alias_gc, $vs, $temppage);
5212                          }
5213                          $temppage = str_replace($alias_ga, $vs, $temppage);
5214                          // replace page group numbers

5215                          $pvs = $this->formatPageNumber($pagegroupnum);
5216                          $pvu = $this->UTF8ToUTF16BE($pvs, false);
5217                          $pk = str_replace('{nb', '{pnb', $k);
5218                          $alias_pga = $this->_escape($pk);
5219                          $alias_pgau = $this->_escape('{'.$pk.'}');
5220                          if ($this->isunicode) {
5221                              $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
5222                              $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
5223                              $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
5224                              $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
5225                          }
5226                          $temppage = str_replace($alias_pgau, $pvu, $temppage);
5227                          if ($this->isunicode) {
5228                              $temppage = str_replace($alias_pgbu, $pvu, $temppage);
5229                              $temppage = str_replace($alias_pgcu, $pvu, $temppage);
5230                              $temppage = str_replace($alias_pgb, $pvs, $temppage);
5231                              $temppage = str_replace($alias_pgc, $pvs, $temppage);
5232                          }
5233                          $temppage = str_replace($alias_pga, $pvs, $temppage);
5234                      }
5235                  }
5236                  if (!empty($this->AliasNbPages)) {
5237                      // replace total pages number

5238                      $temppage = str_replace($alias_au, $nbu, $temppage);
5239                      if ($this->isunicode) {
5240                          $temppage = str_replace($alias_bu, $nbu, $temppage);
5241                          $temppage = str_replace($alias_cu, $nbu, $temppage);
5242                          $temppage = str_replace($alias_b, $nbs, $temppage);
5243                          $temppage = str_replace($alias_c, $nbs, $temppage);
5244                      }
5245                      $temppage = str_replace($alias_a, $nbs, $temppage);
5246                  }
5247                  if (!empty($this->AliasNumPage)) {
5248                      // replace page number

5249                      $pnbs = $this->formatPageNumber($n);
5250                      $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font

5251                      $temppage = str_replace($alias_pau, $pnbu, $temppage);
5252                      if ($this->isunicode) {
5253                          $temppage = str_replace($alias_pbu, $pnbu, $temppage);
5254                          $temppage = str_replace($alias_pcu, $pnbu, $temppage);
5255                          $temppage = str_replace($alias_pb, $pnbs, $temppage);
5256                          $temppage = str_replace($alias_pc, $pnbs, $temppage);
5257                      }
5258                      $temppage = str_replace($alias_pa, $pnbs, $temppage);
5259                  }
5260                  $temppage = str_replace($this->epsmarker, '', $temppage);
5261                  //$this->setPageBuffer($n, $temppage);

5262                  //Page

5263                  $this->_newobj();
5264                  $this->_out('<</Type /Page');
5265                  $this->_out('/Parent 1 0 R');
5266                  $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
5267                  $this->_out('/Resources 2 0 R');
5268                  $this->_putannots($n);
5269                  $this->_out('/Contents '.($this->n + 1).' 0 R>>');
5270                  $this->_out('endobj');
5271                  //Page content

5272                  $p = ($this->compress) ? gzcompress($temppage) : $temppage;
5273                  $this->_newobj();
5274                  $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
5275                  $this->_putstream($p);
5276                  $this->_out('endobj');
5277                  if ($this->diskcache) {
5278                      // remove temporary files

5279                      unlink($this->pages[$n]);
5280                  }
5281              }
5282              //Pages root

5283              $this->offsets[1] = $this->bufferlen;
5284              $this->_out('1 0 obj');
5285              $this->_out('<</Type /Pages');
5286              $kids='/Kids [';
5287              for ($i=0; $i < $nb; ++$i) {
5288                  $kids .= (3 + (2 * $i)).' 0 R ';
5289              }
5290              $this->_out($kids.']');
5291              $this->_out('/Count '.$nb);
5292              //$this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));

5293              $this->_out('>>');
5294              $this->_out('endobj');
5295          }
5296  
5297          /**

5298          * Output Page Annotations.

5299          * !!! THIS FUNCTION IS NOT YET COMPLETED !!!

5300          * See section 8.4 of PDF reference.

5301          * @param int $n page number

5302          * @access protected

5303          * @author Nicola Asuni

5304          * @since 4.0.018 (2008-08-06)

5305          */
5306  		protected function _putannots($n) {
5307              if (isset($this->PageAnnots[$n])) {
5308                  $annots = '/Annots [';
5309                  foreach ($this->PageAnnots[$n] as $key => $pl) {
5310                      $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
5311                      $a = $pl['x'] * $this->k;
5312                      $b = $this->pagedim[$n]['h'] - ($pl['y']  * $this->k);
5313                      $c = $pl['w'] * $this->k;
5314                      $d = $pl['h'] * $this->k;
5315                      $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b-$d);
5316                      $annots .= "\n";
5317                      $annots .= '<</Type /Annot';
5318                      $annots .= ' /Subtype /'.$pl['opt']['subtype'];
5319                      $annots .= ' /Rect ['.$rect.']';
5320                      $annots .= ' /Contents '.$this->_textstring($pl['txt']);
5321                      //$annots .= ' /P ';

5322                      $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
5323                      $annots .= ' /M '.$this->_datastring('D:'.date('YmdHis'));
5324                      if (isset($pl['opt']['f'])) {
5325                          $val = 0;
5326                          if (is_array($pl['opt']['f'])) {
5327                              foreach ($pl['opt']['f'] as $f) {
5328                                  switch (strtolower($f)) {
5329                                      case 'invisible': {
5330                                          $val += 1 << 0;
5331                                          break;
5332                                      }
5333                                      case 'hidden': {
5334                                          $val += 1 << 1;
5335                                          break;
5336                                      }
5337                                      case 'print': {
5338                                          $val += 1 << 2;
5339                                          break;
5340                                      }
5341                                      case 'nozoom': {
5342                                          $val += 1 << 3;
5343                                          break;
5344                                      }
5345                                      case 'norotate': {
5346                                          $val += 1 << 4;
5347                                          break;
5348                                      }
5349                                      case 'noview': {
5350                                          $val += 1 << 5;
5351                                          break;
5352                                      }
5353                                      case 'readonly': {
5354                                          $val += 1 << 6;
5355                                          break;
5356                                      }
5357                                      case 'locked': {
5358                                          $val += 1 << 8;
5359                                          break;
5360                                      }
5361                                      case 'togglenoview': {
5362                                          $val += 1 << 9;
5363                                          break;
5364                                      }
5365                                      case 'lockedcontents': {
5366                                          $val += 1 << 10;
5367                                          break;
5368                                      }
5369                                      default: {
5370                                          break;
5371                                      }
5372                                  }
5373                              }
5374                          }
5375                          $annots .= ' /F '.intval($val);
5376                      }
5377                      //$annots .= ' /AP ';

5378                      //$annots .= ' /AS ';

5379                      $annots .= ' /Border [';
5380                      if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
5381                          $annots .= intval($pl['opt']['border'][0]).' ';
5382                          $annots .= intval($pl['opt']['border'][1]).' ';
5383                          $annots .= intval($pl['opt']['border'][2]);
5384                          if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
5385                              $annots .= ' [';
5386                              foreach ($pl['opt']['border'][3] as $dash) {
5387                                  $annots .= intval($dash).' ';
5388                              }
5389                              $annots .= ']';
5390                          }
5391                      } else {
5392                          $annots .= '0 0 0';
5393                      }
5394                      $annots .= ']';
5395                      if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
5396                          $annots .= ' /BS <<Type /Border';
5397                          if (isset($pl['opt']['bs']['w'])) {
5398                              $annots .= ' /W '.sprintf("%.4F", floatval($pl['opt']['bs']['w']));
5399                          }
5400                          $bstyles = array('S', 'D', 'B', 'I', 'U');
5401                          if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
5402                              $annots .= ' /S /'.$pl['opt']['bs']['s'];
5403                          }
5404                          if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
5405                              $annots .= ' /D [';
5406                              foreach ($pl['opt']['bs']['d'] as $cord) {
5407                                  $cord = floatval($cord);
5408                                  $annots .= sprintf(" %.4F", $cord);
5409                              }
5410                              $annots .= ']';
5411                          }
5412                          $annots .= '>> ';
5413                      }
5414                      if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
5415                          $annots .= ' /BE <<';
5416                          $bstyles = array('S', 'C');
5417                          if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
5418                              $annots .= ' /S /'.$pl['opt']['bs']['s'];
5419                          } else {
5420                              $annots .= ' /S /S';
5421                          }
5422                          if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
5423                              $annots .= ' /I '.sprintf(" %.4F", $pl['opt']['be']['i']);
5424                          }
5425                          $annots .= '>>';
5426                      }
5427                      $annots .= ' /C [';
5428                      if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
5429                          foreach ($pl['opt']['c'] as $col) {
5430                              $col = intval($col);
5431                              $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
5432                              $annots .= sprintf(" %.4F", $color);
5433                          }
5434                      }
5435                      $annots .= ']';
5436                      //$annots .= ' /StructParent ';

5437                      //$annots .= ' /OC ';

5438                      $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
5439                      if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
5440                          // this is a markup type

5441                          if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
5442                              $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
5443                          }
5444                          //$annots .= ' /Popup ';

5445                          if (isset($pl['opt']['ca'])) {
5446                              $annots .= ' /CA '.sprintf("%.4F", floatval($pl['opt']['ca']));
5447                          }
5448                          if (isset($pl['opt']['rc'])) {
5449                              $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5450                          }
5451                          $annots .= ' /CreationDate '.$this->_datastring('D:'.date('YmdHis'));
5452                          //$annots .= ' /IRT ';

5453                          if (isset($pl['opt']['subj'])) {
5454                              $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
5455                          }
5456                          //$annots .= ' /RT ';

5457                          //$annots .= ' /IT ';

5458                          //$annots .= ' /ExData ';

5459                      }
5460                      switch (strtolower($pl['opt']['subtype'])) {
5461                          case 'text': {
5462                              if (isset($pl['opt']['open'])) {
5463                                  $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
5464                              }
5465                              $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
5466                              if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5467                                  $annots .= ' /Name /'.$pl['opt']['name'];
5468                              } else {
5469                                  $annots .= ' /Name /Note';
5470                              }
5471                              $statemodels = array('Marked', 'Review');
5472                              if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
5473                                  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5474                              } else {
5475                                  $pl['opt']['statemodel'] = 'Marked';
5476                                  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5477                              }
5478                              if ($pl['opt']['statemodel'] == 'Marked') {
5479                                  $states = array('Accepted', 'Unmarked');
5480                              } else {
5481                                  $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
5482                              }
5483                              if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
5484                                  $annots .= ' /State /'.$pl['opt']['state'];
5485                              } else {
5486                                  if ($pl['opt']['statemodel'] == 'Marked') {
5487                                      $annots .= ' /State /Unmarked';
5488                                  } else {
5489                                      $annots .= ' /State /None';
5490                                  }
5491                              }
5492                              break;
5493                          }
5494                          case 'link': {
5495                              if(is_string($pl['txt'])) {
5496                                  // external URI link

5497                                  $annots .= ' /A <</S /URI /URI '.$this->_datastring($pl['txt']).'>>';
5498                              } else {
5499                                  // internal link

5500                                  $l = $this->links[$pl['txt']];
5501                                  $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
5502                              }
5503                              $hmodes = array('N', 'I', 'O', 'P');
5504                              if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
5505                                  $annots .= ' /H /'.$pl['opt']['h'];
5506                              } else {
5507                                  $annots .= ' /H /I';
5508                              }
5509                              //$annots .= ' /PA ';

5510                              //$annots .= ' /Quadpoints ';

5511                              break;
5512                          }
5513                          case 'freetext': {
5514                              $annots .= ' /DA '.$this->_textstring($pl['txt']);
5515                              if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
5516                                  $annots .= ' /Q '.intval($pl['opt']['q']);
5517                              }
5518                              if (isset($pl['opt']['rc'])) {
5519                                  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5520                              }
5521                              if (isset($pl['opt']['ds'])) {
5522                                  $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
5523                              }
5524                              if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
5525                                  $annots .= ' /CL [';
5526                                  foreach ($pl['opt']['cl'] as $cl) {
5527                                      $annots .= sprintf("%.4F ", $cl * $this->k);
5528                                  }
5529                                  $annots .= ']';
5530                              }
5531                              $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
5532                              if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
5533                                  $annots .= ' /IT '.$pl['opt']['it'];
5534                              }
5535                              if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
5536                                  $l = $pl['opt']['rd'][0] * $this->k;
5537                                  $r = $pl['opt']['rd'][1] * $this->k;
5538                                  $t = $pl['opt']['rd'][2] * $this->k;
5539                                  $b = $pl['opt']['rd'][3] * $this->k;
5540                                  $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
5541                              }
5542                              //$annots .= ' /LE ';

5543                              break;
5544                          }
5545                          // ... to be completed ...

5546                          case 'line': {
5547                              break;
5548                          }
5549                          case 'square': {
5550                              break;
5551                          }
5552                          case 'circle': {
5553                              break;
5554                          }
5555                          case 'polygon': {
5556                              break;
5557                          }
5558                          case 'polyline': {
5559                              break;
5560                          }
5561                          case 'highlight': {
5562                              break;
5563                          }
5564                          case 'underline': {
5565                              break;
5566                          }
5567                          case 'squiggly': {
5568                              break;
5569                          }
5570                          case 'strikeout': {
5571                              break;
5572                          }
5573                          case 'stamp': {
5574                              break;
5575                          }
5576                          case 'caret': {
5577                              break;
5578                          }
5579                          case 'ink': {
5580                              break;
5581                          }
5582                          case 'popup': {
5583                              break;
5584                          }
5585                          case 'fileattachment': {
5586                              if (!isset($pl['opt']['fs'])) {
5587                                  break;
5588                              }
5589                              $filename = basename($pl['opt']['fs']);
5590                              if (isset($this->embeddedfiles[$filename]['n'])) {
5591                                  $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
5592                                  $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
5593                                  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5594                                      $annots .= ' /Name /'.$pl['opt']['name'];
5595                                  } else {
5596                                      $annots .= ' /Name /PushPin';
5597                                  }
5598                              }
5599                              break;
5600                          }
5601                          case 'sound': {
5602                              if (!isset($pl['opt']['sound'])) {
5603                                  break;
5604                              }
5605                              $filename = basename($pl['opt']['sound']);
5606                              if (isset($this->embeddedfiles[$filename]['n'])) {
5607                                  // ... TO BE COMPLETED ...

5608                                  $iconsapp = array('Speaker', 'Mic');
5609                                  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5610                                      $annots .= ' /Name /'.$pl['opt']['name'];
5611                                  } else {
5612                                      $annots .= ' /Name /Speaker';
5613                                  }
5614                              }
5615                              break;
5616                          }
5617                          case 'movie': {
5618                              break;
5619                          }
5620                          case 'widget': {
5621                              if (isset($pl['opt']['h'])) {
5622                                  $annots .= ' /H '.intval($pl['opt']['h']);
5623                              }
5624                               if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk']))) {
5625                                   $annots .= ' /MK <<';
5626                                   // ... TO BE COMPLETED ...

5627                                   $annots .= '>>';
5628                               }
5629                              break;
5630                          }
5631                          case 'screen': {
5632                              break;
5633                          }
5634                          case 'printermark': {
5635                              break;
5636                          }
5637                          case 'trapnet': {
5638                              break;
5639                          }
5640                          case 'watermark': {
5641                              break;
5642                          }
5643                          case '3d': {
5644                              break;
5645                          }
5646                          default: {
5647                              break;
5648                          }
5649                      }
5650                      
5651                  $annots .= '>>';
5652                  }
5653                  $annots .= "\n]";
5654                  $this->_out($annots);
5655              }
5656          }
5657  
5658          /**

5659          * Output fonts.

5660          * @access protected

5661          */
5662  		protected function _putfonts() {
5663              $nf = $this->n;
5664              foreach ($this->diffs as $diff) {
5665                  //Encodings

5666                  $this->_newobj();
5667                  $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
5668                  $this->_out('endobj');
5669              }
5670              $mqr = get_magic_quotes_runtime();
5671              set_magic_quotes_runtime(0);
5672              foreach ($this->FontFiles as $file => $info) {
5673                  // search and get font file to embedd

5674                  $fontdir = $info['fontdir'];
5675                  $file = strtolower($file);
5676                  $fontfile = '';
5677                  // search files on various directories

5678                  if (file_exists($fontdir.$file)) {
5679                      $fontfile = $fontdir.$file;
5680                  } elseif (file_exists($this->_getfontpath().$file)) {
5681                      $fontfile = $this->_getfontpath().$file;
5682                  } elseif (file_exists($file)) {
5683                      $fontfile = $file;
5684                  }
5685                  if (!$this->empty_string($fontfile)) {
5686                      $font = file_get_contents($fontfile);
5687                      $compressed = (substr($file, -2) == '.z');
5688                      if ((!$compressed) AND (isset($info['length2']))) {
5689                          $header = (ord($font{0}) == 128);
5690                          if ($header) {
5691                              //Strip first binary header

5692                              $font = substr($font, 6);
5693                          }
5694                          if ($header AND (ord($font{$info['length1']}) == 128)) {
5695                              //Strip second binary header

5696                              $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
5697                          }
5698                      }
5699                      $this->_newobj();
5700                      $this->FontFiles[$file]['n'] = $this->n;
5701                      $this->_out('<</Length '.strlen($font));
5702                      if ($compressed) {
5703                          $this->_out('/Filter /FlateDecode');
5704                      }
5705                      $this->_out('/Length1 '.$info['length1']);
5706                      if (isset($info['length2'])) {
5707                          $this->_out('/Length2 '.$info['length2'].' /Length3 0');
5708                      }
5709                      $this->_out('>>');
5710                      $this->_putstream($font);
5711                      $this->_out('endobj');
5712                  }
5713              }
5714              set_magic_quotes_runtime($mqr);
5715              foreach ($this->fontkeys as $k) {
5716                  //Font objects

5717                  $this->setFontSubBuffer($k, 'n', $this->n + 1);
5718                  $font = $this->getFontBuffer($k);
5719                  $type = $font['type'];
5720                  $name = $font['name'];
5721                  if ($type == 'core') {
5722                      //Standard font

5723                      $this->_newobj();
5724                      $this->_out('<</Type /Font');
5725                      $this->_out('/BaseFont /'.$name);
5726                      $this->_out('/Subtype /Type1');
5727                      if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
5728                          $this->_out('/Encoding /WinAnsiEncoding');
5729                      }
5730                      $this->_out('>>');
5731                      $this->_out('endobj');
5732                  } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
5733                      //Additional Type1 or TrueType font

5734                      $this->_newobj();
5735                      $this->_out('<</Type /Font');
5736                      $this->_out('/BaseFont /'.$name);
5737                      $this->_out('/Subtype /'.$type);
5738                      $this->_out('/FirstChar 32 /LastChar 255');
5739                      $this->_out('/Widths '.($this->n + 1).' 0 R');
5740                      $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
5741                      if ($font['enc']) {
5742                          if (isset($font['diff'])) {
5743                              $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
5744                          } else {
5745                              $this->_out('/Encoding /WinAnsiEncoding');
5746                          }
5747                      }
5748                      $this->_out('>>');
5749                      $this->_out('endobj');
5750                      // Widths

5751                      $this->_newobj();
5752                      $cw = &$font['cw'];
5753                      $s = '[';
5754                      for ($i = 32; $i < 256; ++$i) {
5755                          $s .= $cw[$i].' ';
5756                      }
5757                      $this->_out($s.']');
5758                      $this->_out('endobj');
5759                      //Descriptor

5760                      $this->_newobj();
5761                      $s = '<</Type /FontDescriptor /FontName /'.$name;
5762                      foreach ($font['desc'] as $k => $v) {
5763                          $s .= ' /'.$k.' '.$v.'';
5764                      }
5765                      if (!$this->empty_string($font['file'])) {
5766                          $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
5767                      }
5768                      $this->_out($s.'>>');
5769                      $this->_out('endobj');
5770                  } else {
5771                      //Allow for additional types

5772                      $mtd = '_put'.strtolower($type);
5773                      if (!method_exists($this, $mtd)) {
5774                          $this->Error('Unsupported font type: '.$type);
5775                      }
5776                      $this->$mtd($font);
5777                  }
5778              }
5779          }
5780          
5781          /**

5782          * Outputs font widths

5783          * @parameter array $font font data

5784          * @parameter int $cidoffset offset for CID values

5785          * @author Nicola Asuni

5786          * @access protected

5787          * @since 4.4.000 (2008-12-07)

5788          */
5789  		protected function _putfontwidths($font, $cidoffset=0) {
5790              ksort($font['cw']);
5791              $rangeid = 0;
5792              $range = array();
5793              $prevcid = -2;
5794              $prevwidth = -1;
5795              $interval = false;
5796              // for each character

5797              foreach ($font['cw'] as $cid => $width) {
5798                  $cid -= $cidoffset;
5799                  if ($width != $font['dw']) {
5800                      if ($cid == ($prevcid + 1)) {
5801                          // consecutive CID

5802                          if ($width == $prevwidth) {
5803                              if ($width == $range[$rangeid][0]) {
5804                                  $range[$rangeid][] = $width;
5805                              } else {
5806                                  array_pop($range[$rangeid]);
5807                                  // new range

5808                                  $rangeid = $prevcid;
5809                                  $range[$rangeid] = array();
5810                                  $range[$rangeid][] = $prevwidth;
5811                                  $range[$rangeid][] = $width;
5812                              }
5813                              $interval = true;
5814                              $range[$rangeid]['interval'] = true;
5815                          } else {
5816                              if ($interval) {
5817                                  // new range

5818                                  $rangeid = $cid;
5819                                  $range[$rangeid] = array();
5820                                  $range[$rangeid][] = $width;
5821                              } else {
5822                                  $range[$rangeid][] = $width;
5823                              }
5824                              $interval = false;
5825                          }
5826                      } else {
5827                          // new range

5828                          $rangeid = $cid;
5829                          $range[$rangeid] = array();
5830                          $range[$rangeid][] = $width;
5831                          $interval = false;
5832                      }
5833                      $prevcid = $cid;
5834                      $prevwidth = $width;
5835                  }
5836              }
5837              // optimize ranges

5838              $prevk = -1;
5839              $nextk = -1;
5840              $prevint = false;
5841              foreach ($range as $k => $ws) {
5842                  $cws = count($ws);
5843                  if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
5844                      if (isset($range[$k]['interval'])) {
5845                          unset($range[$k]['interval']);
5846                      }
5847                      $range[$prevk] = array_merge($range[$prevk], $range[$k]);
5848                      unset($range[$k]);
5849                  } else {
5850                      $prevk = $k;
5851                  }
5852                  $nextk = $k + $cws;
5853                  if (isset($ws['interval'])) {
5854                      if ($cws > 3) {
5855                          $prevint = true;
5856                      } else {
5857                          $prevint = false;
5858                      }
5859                      unset($range[$k]['interval']);
5860                      --$nextk;
5861                  } else {
5862                      $prevint = false;
5863                  }
5864              }
5865              // output data

5866              $w = '';
5867              foreach ($range as $k => $ws) {
5868                  if (count(array_count_values($ws)) == 1) {
5869                      // interval mode is more compact

5870                      $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
5871                  } else {
5872                      // range mode

5873                      $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
5874                  }
5875              }
5876              $this->_out('/W ['.$w.' ]');
5877          }
5878          
5879          /**

5880          * Adds unicode fonts.<br>

5881          * Based on PDF Reference 1.3 (section 5)

5882          * @parameter array $font font data

5883          * @access protected

5884          * @author Nicola Asuni

5885          * @since 1.52.0.TC005 (2005-01-05)

5886          */
5887  		protected function _puttruetypeunicode($font) {
5888              // Type0 Font

5889              // A composite font composed of other fonts, organized hierarchically

5890              $this->_newobj();
5891              $this->_out('<</Type /Font');
5892              $this->_out('/Subtype /Type0');
5893              $this->_out('/BaseFont /'.$font['name'].'');
5894              $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values.

5895              $this->_out('/ToUnicode /Identity-H');
5896              $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5897              $this->_out('>>');
5898              $this->_out('endobj');
5899              // CIDFontType2

5900              // A CIDFont whose glyph descriptions are based on TrueType font technology

5901              $this->_newobj();
5902              $this->_out('<</Type /Font');
5903              $this->_out('/Subtype /CIDFontType2');
5904              $this->_out('/BaseFont /'.$font['name'].'');
5905              // A dictionary containing entries that define the character collection of the CIDFont.

5906              $cidinfo = '/Registry '.$this->_datastring('Adobe');
5907              $cidinfo .= ' /Ordering '.$this->_datastring('Identity');
5908              $cidinfo .= ' /Supplement 0';
5909              $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
5910              $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
5911              $this->_out('/DW '.$font['dw'].''); // default width

5912              $this->_putfontwidths($font, 0);
5913              $this->_out('/CIDToGIDMap '.($this->n + 2).' 0 R');
5914              $this->_out('>>');
5915              $this->_out('endobj');            
5916              // Font descriptor

5917              // A font descriptor describing the CIDFont default metrics other than its glyph widths

5918              $this->_newobj();
5919              $this->_out('<</Type /FontDescriptor');
5920              $this->_out('/FontName /'.$font['name']);
5921              foreach ($font['desc'] as $key => $value) {
5922                  $this->_out('/'.$key.' '.$value);
5923              }
5924              $fontdir = '';
5925              if (!$this->empty_string($font['file'])) {
5926                  // A stream containing a TrueType font

5927                  $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
5928                  $fontdir = $this->FontFiles[$font['file']]['fontdir'];
5929              }
5930              $this->_out('>>');
5931              $this->_out('endobj');
5932              $this->_newobj();
5933              if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
5934                  // Embed CIDToGIDMap

5935                  // A specification of the mapping from CIDs to glyph indices

5936                  // search and get CTG font file to embedd

5937                  $ctgfile = strtolower($font['ctg']);
5938                  // search and get ctg font file to embedd

5939                  $fontfile = '';
5940                  // search files on various directories

5941                  if (file_exists($fontdir.$ctgfile)) {
5942                      $fontfile = $fontdir.$ctgfile;
5943                  } elseif (file_exists($this->_getfontpath().$ctgfile)) {
5944                      $fontfile = $this->_getfontpath().$ctgfile;
5945                  } elseif (file_exists($ctgfile)) {
5946                      $fontfile = $ctgfile;
5947                  }
5948                  if ($this->empty_string($fontfile)) {
5949                      $this->Error('Font file not found: '.$ctgfile);
5950                  }
5951                  $size = filesize($fontfile);
5952                  $this->_out('<</Length '.$size.'');
5953                  if (substr($fontfile, -2) == '.z') { // check file extension
5954                      // Decompresses data encoded using the public-domain 

5955                      // zlib/deflate compression method, reproducing the 

5956                      // original text or binary data

5957                      $this->_out('/Filter /FlateDecode');
5958                  }
5959                  $this->_out('>>');
5960                  $this->_putstream(file_get_contents($fontfile));
5961              }
5962              $this->_out('endobj');
5963          }
5964          
5965          /**

5966           * Output CID-0 fonts.

5967           * @param array $font font data

5968           * @access protected

5969           * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira

5970           * @since 3.2.000 (2008-06-23)

5971           */
5972  		protected function _putcidfont0($font) {
5973              $cidoffset = 31;
5974              if (isset($font['cidinfo']['uni2cid'])) {
5975                  // convert unicode to cid.

5976                  $uni2cid = $font['cidinfo']['uni2cid'];
5977                  $cw = array();
5978                  foreach ($font['cw'] as $uni => $width) {
5979                      if (isset($uni2cid[$uni])) {
5980                          $cw[($uni2cid[$uni] + $cidoffset)] = $width;
5981                      } elseif ($uni < 256) {
5982                          $cw[$uni] = $width;
5983                      } // else unknown character

5984                  }
5985                  $font = array_merge($font, array('cw' => $cw));
5986              }
5987              $name = $font['name'];
5988              $enc = $font['enc'];
5989              if ($enc) {
5990                  $longname = $name.'-'.$enc;
5991              } else {
5992                  $longname = $name;
5993              }
5994              $this->_newobj();
5995              $this->_out('<</Type /Font');
5996              $this->_out('/BaseFont /'.$longname);
5997              $this->_out('/Subtype /Type0');
5998              if ($enc) {
5999                  $this->_out('/Encoding /'.$enc);
6000              }
6001              $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
6002              $this->_out('>>');
6003              $this->_out('endobj');
6004              $this->_newobj();
6005              $this->_out('<</Type /Font');
6006              $this->_out('/BaseFont /'.$name);
6007              $this->_out('/Subtype /CIDFontType0');
6008              $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
6009              $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
6010              $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
6011              $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
6012              $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
6013              $this->_out('/DW '.$font['dw']);
6014              $this->_putfontwidths($font, $cidoffset);
6015              $this->_out('>>');
6016              $this->_out('endobj');
6017              $this->_newobj();
6018              $s = '<</Type /FontDescriptor /FontName /'.$name;
6019              foreach ($font['desc'] as $k => $v) {
6020                  if ($k != 'Style') {
6021                      $s .= ' /'.$k.' '.$v.'';
6022                  }
6023              }
6024              $this->_out($s.'>>');
6025              $this->_out('endobj');
6026          }
6027  
6028          /**

6029           * Output images.

6030           * @access protected

6031           */
6032  		protected function _putimages() {
6033              $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
6034              foreach ($this->imagekeys as $file) {
6035                  $info = $this->getImageBuffer($file);
6036                  $this->_newobj();
6037                  $this->setImageSubBuffer($file, 'n', $this->n);
6038                  $this->_out('<</Type /XObject');
6039                  $this->_out('/Subtype /Image');
6040                  $this->_out('/Width '.$info['w']);
6041                  $this->_out('/Height '.$info['h']);
6042                  if (isset($info['masked'])) {
6043                      $this->_out('/SMask '.($this->n - 1).' 0 R');
6044                  }
6045                  if ($info['cs'] == 'Indexed') {
6046                      $this->_out('/ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]');
6047                  } else {
6048                      $this->_out('/ColorSpace /'.$info['cs']);
6049                      if ($info['cs'] == 'DeviceCMYK') {
6050                          $this->_out('/Decode [1 0 1 0 1 0 1 0]');
6051                      }
6052                  }
6053                  $this->_out('/BitsPerComponent '.$info['bpc']);
6054                  if (isset($info['f'])) {
6055                      $this->_out('/Filter /'.$info['f']);
6056                  }
6057                  if (isset($info['parms'])) {
6058                      $this->_out($info['parms']);
6059                  }
6060                  if (isset($info['trns']) AND is_array($info['trns'])) {
6061                      $trns='';
6062                      $count_info = count($info['trns']);
6063                      for ($i=0; $i < $count_info; ++$i) {
6064                          $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
6065                      }
6066                      $this->_out('/Mask ['.$trns.']');
6067                  }
6068                  $this->_out('/Length '.strlen($info['data']).'>>');
6069                  $this->_putstream($info['data']);
6070                  $this->_out('endobj');
6071                  //Palette

6072                  if ($info['cs'] == 'Indexed') {
6073                      $this->_newobj();
6074                      $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
6075                      $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
6076                      $this->_putstream($pal);
6077                      $this->_out('endobj');
6078                  }
6079              }
6080          }
6081  
6082          /**

6083          * Output Spot Colors Resources.

6084          * @access protected

6085          * @since 4.0.024 (2008-09-12)

6086          */
6087  		protected function _putspotcolors() {
6088              foreach ($this->spot_colors as $name => $color) {
6089                  $this->_newobj();
6090                  $this->spot_colors[$name]['n'] = $this->n;
6091                  $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
6092                  $this->_out('/DeviceCMYK <<');
6093                  $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
6094                  $this->_out(sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
6095                  $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
6096                  $this->_out('endobj');
6097              }
6098          }
6099  
6100          /**

6101          * Output object dictionary for images.

6102          * @access protected

6103          */
6104  		protected function _putxobjectdict() {
6105              foreach ($this->imagekeys as $file) {
6106                  $info = $this->getImageBuffer($file);
6107                  $this->_out('/I'.$info['i'].' '.$info['n'].' 0 R');
6108              }
6109          }
6110  
6111          /**

6112          * Output Resources Dictionary.

6113          * @access protected

6114          */
6115  		protected function _putresourcedict() {
6116              $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
6117              $this->_out('/Font <<');
6118              foreach ($this->fontkeys as $fontkey) {
6119                  $font = $this->getFontBuffer($fontkey);
6120                  $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
6121              }
6122              $this->_out('>>');
6123              $this->_out('/XObject <<');
6124              $this->_putxobjectdict();
6125              $this->_out('>>');
6126              // visibility

6127              $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
6128              // transparency

6129              $this->_out('/ExtGState <<');
6130              foreach ($this->extgstates as $k => $extgstate) {
6131                  $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
6132              }
6133              $this->_out('>>');
6134              // gradients

6135              if (isset($this->gradients) AND (count($this->gradients) > 0)) {
6136                  $this->_out('/Shading <<');
6137                  foreach ($this->gradients as $id => $grad) {
6138                      $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
6139                  }
6140                  $this->_out('>>');
6141              }
6142              // spot colors

6143              if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
6144                  $this->_out('/ColorSpace <<');
6145                  foreach ($this->spot_colors as $color) {
6146                      $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
6147                  }
6148                  $this->_out('>>');
6149              }
6150          }
6151          
6152          /**

6153          * Output Resources.

6154          * @access protected

6155          */
6156  		protected function _putresources() {
6157              $this->_putextgstates();
6158              $this->_putocg();
6159              $this->_putfonts();
6160              $this->_putimages();
6161              $this->_putspotcolors();
6162              $this->_putshaders();
6163              //Resource dictionary

6164              $this->offsets[2] = $this->bufferlen;
6165              $this->_out('2 0 obj');
6166              $this->_out('<<');
6167              $this->_putresourcedict();
6168              $this->_out('>>');
6169              $this->_out('endobj');
6170              $this->_putjavascript();
6171              $this->_putbookmarks();
6172              $this->_putEmbeddedFiles();
6173              // encryption

6174              if ($this->encrypted) {
6175                  $this->_newobj();
6176                  $this->enc_obj_id = $this->n;
6177                  $this->_out('<<');
6178                  $this->_putencryption();
6179                  $this->_out('>>');
6180                  $this->_out('endobj');
6181              }
6182          }
6183          
6184          /**

6185          * Adds some Metadata information

6186          * (see Chapter 10.2 of PDF Reference)

6187          * @access protected

6188          */
6189  		protected function _putinfo() {
6190              if (!$this->empty_string($this->title)) {
6191                  $this->_out('/Title '.$this->_textstring($this->title));
6192              }
6193              if (!$this->empty_string($this->author)) {
6194                  $this->_out('/Author '.$this->_textstring($this->author));
6195              }
6196              if (!$this->empty_string($this->subject)) {
6197                  $this->_out('/Subject '.$this->_textstring($this->subject));
6198              }
6199              if (!$this->empty_string($this->keywords)) {
6200                  $this->_out('/Keywords '.$this->_textstring($this->keywords));
6201              }
6202              if (!$this->empty_string($this->creator)) {
6203                  $this->_out('/Creator '.$this->_textstring($this->creator));
6204              }
6205              if (defined('PDF_PRODUCER')) {
6206                  $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
6207              }
6208              $this->_out('/CreationDate '.$this->_datastring('D:'.date('YmdHis')));
6209              $this->_out('/ModDate '.$this->_datastring('D:'.date('YmdHis')));    
6210          }
6211          
6212          /**

6213          * Output Catalog.

6214          * @access protected

6215          */
6216  		protected function _putcatalog() {
6217              $this->_out('/Type /Catalog');
6218              $this->_out('/Pages 1 0 R');
6219              if ($this->ZoomMode == 'fullpage') {
6220                  $this->_out('/OpenAction [3 0 R /Fit]');
6221              } elseif ($this->ZoomMode == 'fullwidth') {
6222                  $this->_out('/OpenAction [3 0 R /FitH null]');
6223              } elseif ($this->ZoomMode == 'real') {
6224                  $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
6225              } elseif (!is_string($this->ZoomMode)) {
6226                  $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
6227              }
6228              if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
6229                  $this->_out('/PageLayout /'.$this->LayoutMode.'');
6230              }
6231              if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
6232                  $this->_out('/PageMode /'.$this->PageMode);
6233              }
6234              if (isset($this->l['a_meta_language'])) {
6235                  $this->_out('/Lang /'.$this->l['a_meta_language']);
6236              }
6237              $this->_out('/Names <<');
6238              if (!$this->empty_string($this->javascript)) {
6239                  $this->_out('/JavaScript '.($this->n_js).' 0 R');
6240              }
6241              $this->_out('>>');
6242              if (count($this->outlines) > 0) {
6243                  $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
6244                  $this->_out('/PageMode /UseOutlines');
6245              }
6246              $this->_putviewerpreferences();
6247              $p = $this->n_ocg_print.' 0 R';
6248              $v = $this->n_ocg_view.' 0 R';
6249              $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>';
6250              $this->_out('/OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>');
6251          }
6252          
6253          /**

6254          * Output viewer preferences.

6255          * @author Nicola asuni

6256          * @since 3.1.000 (2008-06-09)

6257          * @access protected

6258          */
6259  		protected function _putviewerpreferences() {
6260              $this->_out('/ViewerPreferences<<');
6261              if ($this->rtl) {
6262                  $this->_out('/Direction /R2L');
6263              } else {
6264                  $this->_out('/Direction /L2R');
6265              }
6266              if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
6267                  $this->_out('/HideToolbar true');
6268              }
6269              if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
6270                  $this->_out('/HideMenubar true');
6271              }
6272              if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
6273                  $this->_out('/HideWindowUI true');
6274              }
6275              if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
6276                  $this->_out('/FitWindow true');
6277              }
6278              if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
6279                  $this->_out('/CenterWindow true');
6280              }
6281              if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
6282                  $this->_out('/DisplayDocTitle true');
6283              }
6284              if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
6285                  $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
6286              }
6287              if (isset($this->viewer_preferences['ViewArea'])) {
6288                  $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
6289              }
6290              if (isset($this->viewer_preferences['ViewClip'])) {
6291                  $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
6292              }
6293              if (isset($this->viewer_preferences['PrintArea'])) {
6294                  $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
6295              }
6296              if (isset($this->viewer_preferences['PrintClip'])) {
6297                  $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
6298              }
6299              if (isset($this->viewer_preferences['PrintScaling'])) {
6300                  $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
6301              }
6302              if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
6303                  $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
6304              }
6305              if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
6306                  if ($this->viewer_preferences['PickTrayByPDFSize']) {
6307                      $this->_out('/PickTrayByPDFSize true');
6308                  } else {
6309                      $this->_out('/PickTrayByPDFSize false');
6310                  }
6311              }
6312              if (isset($this->viewer_preferences['PrintPageRange'])) {
6313                  $PrintPageRangeNum = '';
6314                  foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
6315                      $PrintPageRangeNum .= ' '.($v - 1).'';
6316                  }
6317                  $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
6318              }
6319              if (isset($this->viewer_preferences['NumCopies'])) {
6320                  $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
6321              }
6322              $this->_out('>>');
6323          }
6324  
6325          /**

6326          * Output trailer.

6327          * @access protected

6328          */
6329  		protected function _puttrailer() {
6330              $this->_out('/Size '.($this->n + 1));
6331              $this->_out('/Root '.$this->n.' 0 R');
6332              $this->_out('/Info '.($this->n - 1).' 0 R');
6333              if ($this->encrypted) {
6334                  $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
6335                  $this->_out('/ID [()()]');
6336              }
6337          }
6338  
6339          /**

6340          * Output PDF header.

6341          * @access protected

6342          */
6343  		protected function _putheader() {
6344              $this->_out('%PDF-'.$this->PDFVersion);
6345          }
6346  
6347          /**

6348          * Output end of document (EOF).

6349          * @access protected

6350          */
6351  		protected function _enddoc() {
6352              $this->state = 1;
6353              $this->_putheader();
6354              $this->_putpages();
6355              $this->_putresources();
6356              //Info

6357              $this->_newobj();
6358              $this->_out('<<');
6359              $this->_putinfo();
6360              $this->_out('>>');
6361              $this->_out('endobj');
6362              //Catalog

6363              $this->_newobj();
6364              $this->_out('<<');
6365              $this->_putcatalog();
6366              $this->_putcertification();
6367              $this->_putuserrights();
6368              $this->_out('>>');
6369              $this->_out('endobj');
6370              //Cross-ref

6371              $o = $this->bufferlen;
6372              $this->_out('xref');
6373              $this->_out('0 '.($this->n + 1));
6374              $this->_out('0000000000 65535 f ');
6375              for ($i=1; $i <= $this->n; ++$i) {
6376                  $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
6377              }
6378              if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) {
6379                  $this->_out('100000 '.count($this->embeddedfiles));
6380                  foreach ($this->embeddedfiles as $filename => $filedata) {
6381                      $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']]));
6382                  }
6383              }
6384              //Trailer

6385              $this->_out('trailer');
6386              $this->_out('<<');
6387              $this->_puttrailer();
6388              $this->_out('>>');
6389              $this->_out('startxref');
6390              $this->_out($o);
6391              $this->_out('%%EOF');
6392              $this->state = 3; // end-of-doc

6393              if ($this->diskcache) {
6394                  // remove temporary files used for images

6395                  foreach ($this->imagekeys as $key) {
6396                      // remove temporary files

6397                      unlink($this->images[$key]);
6398                  }
6399                  foreach ($this->fontkeys as $key) {
6400                      // remove temporary files

6401                      unlink($this->fonts[$key]);
6402                  }
6403              }
6404          }
6405  
6406          /**

6407          * Initialize a new page.

6408          * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>

6409          * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>

6410          * @access protected

6411          */
6412  		protected function _beginpage($orientation='', $format='') {
6413              ++$this->page;
6414              $this->setPageBuffer($this->page, '');
6415              // initialize array for graphics tranformation positions inside a page buffer

6416              $this->transfmrk[$this->page] = array();
6417              $this->state = 2;
6418              if ($this->empty_string($orientation)) {
6419                  if (isset($this->CurOrientation)) {
6420                      $orientation = $this->CurOrientation;
6421                  } else {
6422                      $orientation = 'P';
6423                  }
6424              }
6425              if ($this->empty_string($format)) {
6426                  $this->setPageOrientation($orientation);
6427              } else {
6428                  $this->setPageFormat($format, $orientation);
6429              }
6430              if ($this->rtl) {
6431                  $this->x = $this->w - $this->rMargin;
6432              } else {
6433                  $this->x = $this->lMargin;
6434              }
6435              $this->y = $this->tMargin;
6436              if (isset($this->newpagegroup[$this->page])) {
6437                  // start a new group

6438                  $n = sizeof($this->pagegroups) + 1;
6439                  $alias = '{nb'.$n.'}';
6440                  $this->pagegroups[$alias] = 1;
6441                  $this->currpagegroup = $alias;
6442              } elseif ($this->currpagegroup) {
6443                  ++$this->pagegroups[$this->currpagegroup];
6444              }
6445          }
6446  
6447          /**

6448          * Mark end of page.

6449          * @access protected

6450          */
6451  		protected function _endpage() {
6452              $this->setVisibility('all');
6453              $this->state = 1;
6454          }
6455  
6456          /**

6457          * Begin a new object.

6458          * @access protected

6459          */
6460  		protected function _newobj() {
6461              ++$this->n;
6462              $this->offsets[$this->n] = $this->bufferlen;
6463              $this->_out($this->n.' 0 obj');
6464          }
6465  
6466          /**

6467          * Underline text.

6468          * @param int $x X coordinate

6469          * @param int $y Y coordinate

6470          * @param string $txt text to underline

6471          * @access protected

6472          */
6473  		protected function _dounderline($x, $y, $txt) {
6474              $up = $this->CurrentFont['up'];
6475              $ut = $this->CurrentFont['ut'];
6476              $w = $this->GetStringWidth($txt);
6477              return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
6478          }
6479          
6480          /**

6481          * Line through text.

6482          * @param int $x X coordinate

6483          * @param int $y Y coordinate

6484          * @param string $txt text to linethrough

6485          * @access protected

6486          */
6487  		protected function _dolinethrough($x, $y, $txt) {
6488              $up = $this->CurrentFont['up'];
6489              $ut = $this->CurrentFont['ut'];
6490              $w = $this->GetStringWidth($txt);
6491              return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - ($this->FontSize/2) - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
6492          }
6493          
6494          /**

6495          * Read a 4-byte integer from file.

6496          * @param string $f file name.

6497          * @return 4-byte integer

6498          * @access protected

6499          */
6500  		protected function _freadint($f) {
6501              $a = unpack('Ni', fread($f, 4));
6502              return $a['i'];
6503          }
6504          
6505          /**

6506          * Add "\" before "\", "(" and ")"

6507          * @param string $s string to escape.

6508          * @return string escaped string.

6509          * @access protected

6510          */
6511  		protected function _escape($s) {
6512              // the chr(13) substitution fixes the Bugs item #1421290.

6513              return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
6514          }
6515          
6516          /**

6517          * Format a date string for meta information

6518          * @param string $s date string to escape.

6519          * @return string escaped string.

6520          * @access protected

6521          */
6522  		protected function _datastring($s) {
6523              if ($this->encrypted) {
6524                  $s = $this->_RC4($this->_objectkey($this->n), $s);
6525              }
6526              return '('. $this->_escape($s).')';
6527          }
6528          
6529          /**

6530          * Format a text string for meta information

6531          * @param string $s string to escape.

6532          * @return string escaped string.

6533          * @access protected

6534          */
6535  		protected function _textstring($s) {
6536              if ($this->isunicode) {
6537                  //Convert string to UTF-16BE

6538                  $s = $this->UTF8ToUTF16BE($s, true);
6539              }
6540              return $this->_datastring($s);
6541          }
6542                  
6543          /**

6544          * Format a text string

6545          * @param string $s string to escape.

6546          * @return string escaped string.

6547          * @access protected

6548          */
6549  		protected function _escapetext($s) {
6550              if ($this->isunicode) {
6551                  if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
6552                      $s = $this->UTF8ToLatin1($s);
6553                  } else {
6554                      //Convert string to UTF-16BE and reverse RTL language

6555                      $s = $this->utf8StrRev($s, false, $this->tmprtl);
6556                  }
6557              }
6558              return $this->_escape($s);
6559          }
6560          
6561          /**

6562          * Output a stream.

6563          * @param string $s string to output.

6564          * @access protected

6565          */
6566  		protected function _putstream($s) {
6567              if ($this->encrypted) {
6568                  $s = $this->_RC4($this->_objectkey($this->n), $s);
6569              }
6570              $this->_out('stream');
6571              $this->_out($s);
6572              $this->_out('endstream');
6573          }
6574          
6575          /**

6576          * Output a string to the document.

6577          * @param string $s string to output.

6578          * @access protected

6579          */
6580  		protected function _out($s) {
6581              if ($this->state == 2) {
6582                  if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
6583                      // puts data before page footer

6584                      $pagebuff = $this->getPageBuffer($this->page);
6585                      $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
6586                      $footer = substr($pagebuff, -$this->footerlen[$this->page]);
6587                      $this->setPageBuffer($this->page, $page.$s."\n".$footer);
6588                      // update footer position

6589                      $this->footerpos[$this->page] += strlen($s."\n");    
6590                  } else {
6591                      $this->setPageBuffer($this->page, $s."\n", true);
6592                  }
6593              } else {
6594                  $this->setBuffer($s."\n");
6595              }
6596          }
6597          
6598           /**

6599           * Converts UTF-8 strings to codepoints array.<br>

6600           * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>

6601           * Based on: http://www.faqs.org/rfcs/rfc3629.html

6602           * <pre>

6603           *       Char. number range  |        UTF-8 octet sequence

6604           *       (hexadecimal)    |              (binary)

6605           *    --------------------+-----------------------------------------------

6606           *    0000 0000-0000 007F | 0xxxxxxx

6607           *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx

6608           *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

6609           *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

6610           *    ---------------------------------------------------------------------

6611           *

6612           *   ABFN notation:

6613           *   ---------------------------------------------------------------------

6614           *   UTF8-octets = *( UTF8-char )

6615           *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4

6616           *   UTF8-1      = %x00-7F

6617           *   UTF8-2      = %xC2-DF UTF8-tail

6618           *

6619           *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /

6620           *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )

6621           *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /

6622           *                 %xF4 %x80-8F 2( UTF8-tail )

6623           *   UTF8-tail   = %x80-BF

6624           *   ---------------------------------------------------------------------

6625           * </pre>

6626           * @param string $str string to process.

6627           * @return array containing codepoints (UTF-8 characters values)

6628           * @access protected

6629           * @author Nicola Asuni

6630           * @since 1.53.0.TC005 (2005-01-05)

6631           */
6632  		protected function UTF8StringToArray($str) {
6633              if (isset($this->cache_UTF8StringToArray['_'.$str])) {
6634                  // return cached value

6635                  return($this->cache_UTF8StringToArray['_'.$str]);
6636              }
6637              // check cache size

6638              if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
6639                  // remove first element

6640                  array_shift($this->cache_UTF8StringToArray);
6641              }
6642              ++$this->cache_size_UTF8StringToArray;
6643              if (!$this->isunicode) {
6644                  // split string into array of equivalent codes

6645                  $strarr = array();
6646                  $strlen = strlen($str);
6647                  for ($i=0; $i < $strlen; ++$i) {
6648                      $strarr[] = ord($str{$i});
6649                  }
6650                  // insert new value on cache

6651                  $this->cache_UTF8StringToArray['_'.$str] = $strarr;
6652                  return $strarr;
6653              }
6654              $unicode = array(); // array containing unicode values

6655              $bytes  = array(); // array containing single character byte sequences

6656              $numbytes  = 1; // number of octetc needed to represent the UTF-8 character

6657              $str .= ''; // force $str to be a string

6658              $length = strlen($str);
6659              for ($i = 0; $i < $length; ++$i) {
6660                  $char = ord($str{$i}); // get one string character at time

6661                  if (count($bytes) == 0) { // get starting octect
6662                      if ($char <= 0x7F) {
6663                          $unicode[] = $char; // use the character "as is" because is ASCII

6664                          $numbytes = 1;
6665                      } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
6666                          $bytes[] = ($char - 0xC0) << 0x06; 
6667                          $numbytes = 2;
6668                      } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
6669                          $bytes[] = ($char - 0xE0) << 0x0C; 
6670                          $numbytes = 3;
6671                      } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
6672                          $bytes[] = ($char - 0xF0) << 0x12; 
6673                          $numbytes = 4;
6674                      } else {
6675                          // use replacement character for other invalid sequences

6676                          $unicode[] = 0xFFFD;
6677                          $bytes = array();
6678                          $numbytes = 1;
6679                      }
6680                  } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
6681                      $bytes[] = $char - 0x80;
6682                      if (count($bytes) == $numbytes) {
6683                          // compose UTF-8 bytes to a single unicode value

6684                          $char = $bytes[0];
6685                          for ($j = 1; $j < $numbytes; ++$j) {
6686                              $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
6687                          }
6688                          if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
6689                              /* The definition of UTF-8 prohibits encoding character numbers between

6690                              U+D800 and U+DFFF, which are reserved for use with the UTF-16

6691                              encoding form (as surrogate pairs) and do not directly represent

6692                              characters. */
6693                              $unicode[] = 0xFFFD; // use replacement character

6694                          } else {
6695                              $unicode[] = $char; // add char to array

6696                          }
6697                          // reset data for next char

6698                          $bytes = array(); 
6699                          $numbytes = 1;
6700                      }
6701                  } else {
6702                      // use replacement character for other invalid sequences

6703                      $unicode[] = 0xFFFD;
6704                      $bytes = array();
6705                      $numbytes = 1;
6706                  }
6707              }
6708              // insert new value on cache

6709              $this->cache_UTF8StringToArray['_'.$str] = $unicode;
6710              return $unicode;
6711          }
6712          
6713          /**

6714           * Converts UTF-8 strings to UTF16-BE.<br>

6715           * @param string $str string to process.

6716           * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)

6717           * @return string

6718           * @access protected

6719           * @author Nicola Asuni

6720           * @since 1.53.0.TC005 (2005-01-05)

6721           * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()

6722           */
6723  		protected function UTF8ToUTF16BE($str, $setbom=true) {
6724              if (!$this->isunicode) {
6725                  return $str; // string is not in unicode

6726              }
6727              $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values

6728              return $this->arrUTF8ToUTF16BE($unicode, $setbom);
6729          }
6730          
6731          /**

6732           * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>

6733           * @param string $str string to process.

6734           * @return string

6735           * @author Andrew Whitehead, Nicola Asuni

6736           * @access protected

6737           * @since 3.2.000 (2008-06-23)

6738           */
6739  		protected function UTF8ToLatin1($str) {
6740              global $utf8tolatin;
6741              if (!$this->isunicode) {
6742                  return $str; // string is not in unicode

6743              }
6744              $outstr = ''; // string to be returned

6745              $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values

6746              foreach ($unicode as $char) {
6747                  if ($char < 256) {
6748                      $outstr .= chr($char);
6749                  } elseif (array_key_exists($char, $utf8tolatin)) {
6750                      // map from UTF-8

6751                      $outstr .= chr($utf8tolatin[$char]);
6752                  } elseif ($char == 0xFFFD) {
6753                      // skip

6754                  } else {
6755                      $outstr .= '?';
6756                  }
6757              }
6758              return $outstr;
6759          }
6760  
6761          /**

6762           * Converts array of UTF-8 characters to UTF16-BE string.<br>

6763           * Based on: http://www.faqs.org/rfcs/rfc2781.html

6764            * <pre>

6765           *   Encoding UTF-16:

6766           * 

6767            *   Encoding of a single character from an ISO 10646 character value to

6768           *    UTF-16 proceeds as follows. Let U be the character number, no greater

6769           *    than 0x10FFFF.

6770           * 

6771           *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and

6772           *       terminate.

6773           * 

6774           *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,

6775           *       U' must be less than or equal to 0xFFFFF. That is, U' can be

6776           *       represented in 20 bits.

6777           * 

6778           *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and

6779           *       0xDC00, respectively. These integers each have 10 bits free to

6780           *       encode the character value, for a total of 20 bits.

6781           * 

6782           *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order

6783           *       bits of W1 and the 10 low-order bits of U' to the 10 low-order

6784           *       bits of W2. Terminate.

6785           * 

6786           *    Graphically, steps 2 through 4 look like:

6787           *    U' = yyyyyyyyyyxxxxxxxxxx

6788           *    W1 = 110110yyyyyyyyyy

6789           *    W2 = 110111xxxxxxxxxx

6790           * </pre>

6791           * @param array $unicode array containing UTF-8 unicode values

6792           * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)

6793           * @return string

6794           * @access protected

6795           * @author Nicola Asuni

6796           * @since 2.1.000 (2008-01-08)

6797           * @see UTF8ToUTF16BE()

6798           */
6799  		protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
6800              $outstr = ''; // string to be returned

6801              if ($setbom) {
6802                  $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)

6803              }
6804              foreach ($unicode as $char) {
6805                  if ($char == 0xFFFD) {
6806                      $outstr .= "\xFF\xFD"; // replacement character

6807                  } elseif ($char < 0x10000) {
6808                      $outstr .= chr($char >> 0x08);
6809                      $outstr .= chr($char & 0xFF);
6810                  } else {
6811                      $char -= 0x10000;
6812                      $w1 = 0xD800 | ($char >> 0x10);
6813                      $w2 = 0xDC00 | ($char & 0x3FF);    
6814                      $outstr .= chr($w1 >> 0x08);
6815                      $outstr .= chr($w1 & 0xFF);
6816                      $outstr .= chr($w2 >> 0x08);
6817                      $outstr .= chr($w2 & 0xFF);
6818                  }
6819              }
6820              return $outstr;
6821          }
6822          // ====================================================

6823          
6824          /**

6825            * Set header font.

6826           * @param array $font font

6827           * @access public

6828           * @since 1.1

6829           */
6830  		public function setHeaderFont($font) {
6831              $this->header_font = $font;
6832          }
6833          
6834          /**

6835            * Get header font.

6836            * @return array()

6837           * @access public

6838           * @since 4.0.012 (2008-07-24)

6839           */
6840  		public function getHeaderFont() {
6841              return $this->header_font;
6842          }
6843          
6844          /**

6845            * Set footer font.

6846           * @param array $font font

6847           * @access public

6848           * @since 1.1

6849           */
6850  		public function setFooterFont($font) {
6851              $this->footer_font = $font;
6852          }
6853          
6854          /**

6855            * Get Footer font.

6856            * @return array()

6857           * @access public

6858           * @since 4.0.012 (2008-07-24)

6859           */
6860  		public function getFooterFont() {
6861              return $this->footer_font;
6862          }
6863          
6864          /**

6865            * Set language array.

6866           * @param array $language

6867           * @access public

6868           * @since 1.1

6869           */
6870  		public function setLanguageArray($language) {
6871              $this->l = $language;
6872              $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
6873          }
6874          
6875          /**

6876           * Returns the PDF data.

6877           * @access public

6878           */
6879  		public function getPDFData() {
6880              if ($this->state < 3) {
6881                  $this->Close();
6882              }
6883              return $this->buffer;
6884          }
6885                  
6886          /**

6887           * Output anchor link.

6888           * @param string $url link URL or internal link (i.e.: &lt;a href="#23"&gt;link to page 23&lt;/a&gt;)

6889           * @param string $name link name

6890           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.

6891           * @param boolean $firstline if true prints only the first line and return the remaining string.

6892           * @param array $color array of RGB text color

6893           * @param string $style font style (U, D, B, I)

6894           * @return the number of cells used or the remaining text if $firstline = true;

6895           * @access public

6896           */
6897  		public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) {
6898              if (!$this->empty_string($url) AND ($url{0} == '#')) {
6899                  // convert url to internal link

6900                  $page = intval(substr($url, 1));
6901                  $url = $this->AddLink();
6902                  $this->SetLink($url, 0, $page);
6903              }
6904              // store current settings

6905              $prevcolor = $this->fgcolor;
6906              $prevstyle = $this->FontStyle;
6907              if (empty($color)) {
6908                  $this->SetTextColorArray($this->htmlLinkColorArray);
6909              } else {
6910                  $this->SetTextColorArray($color);
6911              }
6912              if ($style == -1) {
6913                  $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
6914              } else {
6915                  $this->SetFont('', $this->FontStyle.$style);
6916              }
6917              $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
6918              // restore settings

6919              $this->SetFont('', $prevstyle);
6920              $this->SetTextColorArray($prevcolor);
6921              return $ret;
6922          }
6923          
6924          /**

6925           * Returns an associative array (keys: R,G,B) from an html color name or a six-digit or three-digit hexadecimal color representation (i.e. #3FE5AA or #7FF).

6926           * @param string $color html color 

6927           * @return array RGB color or false in case of error.

6928           * @access public

6929           */        
6930  		public function convertHTMLColorToDec($color='#FFFFFF') {
6931              global $webcolor;
6932              $returncolor = false;
6933              $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces

6934              $color = strtolower($color);
6935              if (strlen($color) == 0) {
6936                  return false;
6937              }
6938              if (substr($color, 0, 3) == 'rgb') {
6939                  $codes = substr($color, 4);
6940                  $codes = str_replace(')', '', $codes);
6941                  $returncolor = explode(',', $codes, 3);
6942                  return $returncolor;
6943              }
6944              if (substr($color, 0, 1) != '#') {
6945                  // decode color name

6946                  if (isset($webcolor[$color])) {
6947                      $color_code = $webcolor[$color];
6948                  } else {
6949                      return false;
6950                  }
6951              } else {
6952                  $color_code = substr($color, 1);
6953              }
6954              switch (strlen($color_code)) {
6955                  case 3: {
6956                      // three-digit hexadecimal representation

6957                      $r = substr($color_code, 0, 1);
6958                      $g = substr($color_code, 1, 1);
6959                      $b = substr($color_code, 2, 1);
6960                      $returncolor['R'] = hexdec($r.$r);
6961                      $returncolor['G'] = hexdec($g.$g);
6962                      $returncolor['B'] = hexdec($b.$b);
6963                      break;
6964                  }
6965                  case 6: {
6966                      // six-digit hexadecimal representation

6967                      $returncolor['R'] = hexdec(substr($color_code, 0, 2));
6968                      $returncolor['G'] = hexdec(substr($color_code, 2, 2));
6969                      $returncolor['B'] = hexdec(substr($color_code, 4, 2));
6970                      break;
6971                  }
6972              }
6973              return $returncolor;
6974          }
6975          
6976          /**

6977           * Converts pixels to User's Units.

6978           * @param int $px pixels

6979           * @return float value in user's unit

6980           * @access public

6981           * @see setImageScale(), getImageScale()

6982           */
6983  		public function pixelsToUnits($px) {
6984              return ($px / ($this->imgscale * $this->k));
6985          }
6986              
6987          /**

6988           * Reverse function for htmlentities. 

6989           * Convert entities in UTF-8.

6990           * @param $text_to_convert Text to convert.

6991           * @return string converted

6992           * @access public

6993          */
6994  		public function unhtmlentities($text_to_convert) {
6995              return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
6996          }
6997          
6998          // ENCRYPTION METHODS ----------------------------------

6999          // SINCE 2.0.000 (2008-01-02)

7000          
7001          /**

7002          * Compute encryption key depending on object number where the encrypted data is stored

7003          * @param int $n object number

7004          * @access protected

7005          * @since 2.0.000 (2008-01-02)

7006          */
7007  		protected function _objectkey($n) {
7008              return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10);
7009          }
7010          
7011          /**

7012           * Put encryption on PDF document.

7013           * @access protected

7014           * @since 2.0.000 (2008-01-02)

7015           */
7016  		protected function _putencryption() {
7017              $this->_out('/Filter /Standard');
7018              $this->_out('/V 1');
7019              $this->_out('/R 2');
7020              $this->_out('/O ('.$this->_escape($this->Ovalue).')');
7021              $this->_out('/U ('.$this->_escape($this->Uvalue).')');
7022              $this->_out('/P '.$this->Pvalue);
7023          }
7024          
7025          /**

7026          * Returns the input text exrypted using RC4 algorithm and the specified key.

7027          * RC4 is the standard encryption algorithm used in PDF format

7028          * @param string $key encryption key

7029          * @param String $text input text to be encrypted

7030          * @return String encrypted text

7031          * @access protected

7032          * @since 2.0.000 (2008-01-02)

7033          * @author Klemen Vodopivec

7034          */
7035  		protected function _RC4($key, $text) {
7036              if ($this->last_rc4_key != $key) {
7037                  $k = str_repeat($key, ((256 / strlen($key)) + 1));
7038                  $rc4 = range(0, 255);
7039                  $j = 0;
7040                  for ($i = 0; $i < 256; ++$i) {
7041                      $t = $rc4[$i];
7042                      $j = ($j + $t + ord($k{$i})) % 256;
7043                      $rc4[$i] = $rc4[$j];
7044                      $rc4[$j] = $t;
7045                  }
7046                  $this->last_rc4_key = $key;
7047                  $this->last_rc4_key_c = $rc4;
7048              } else {
7049                  $rc4 = $this->last_rc4_key_c;
7050              }
7051              $len = strlen($text);
7052              $a = 0;
7053              $b = 0;
7054              $out = '';
7055              for ($i = 0; $i < $len; ++$i) {
7056                  $a = ($a + 1) % 256;
7057                  $t = $rc4[$a];
7058                  $b = ($b + $t) % 256;
7059                  $rc4[$a] = $rc4[$b];
7060                  $rc4[$b] = $t;
7061                  $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
7062                  $out .= chr(ord($text{$i}) ^ $k);
7063              }
7064              return $out;
7065          }
7066          
7067          /**

7068          * Encrypts a string using MD5 and returns it's value as a binary string.

7069          * @param string $str input string

7070          * @return String MD5 encrypted binary string

7071          * @access protected

7072          * @since 2.0.000 (2008-01-02)

7073          * @author Klemen Vodopivec

7074          */
7075  		protected function _md5_16($str) {
7076              return pack('H*', md5($str));
7077          }
7078          
7079          /**

7080          * Compute O value (used for RC4 encryption)

7081          * @param String $user_pass user password

7082          * @param String $owner_pass user password

7083          * @return String O value

7084          * @access protected

7085          * @since 2.0.000 (2008-01-02)

7086          * @author Klemen Vodopivec

7087          */
7088  		protected function _Ovalue($user_pass, $owner_pass) {
7089              $tmp = $this->_md5_16($owner_pass);
7090              $owner_RC4_key = substr($tmp, 0, 5);
7091              return $this->_RC4($owner_RC4_key, $user_pass);
7092          }
7093          
7094          /**

7095          * Compute U value (used for RC4 encryption)

7096          * @return String U value

7097          * @access protected

7098          * @since 2.0.000 (2008-01-02)

7099          * @author Klemen Vodopivec

7100          */
7101  		protected function _Uvalue() {
7102              return $this->_RC4($this->encryption_key, $this->padding);
7103          }
7104          
7105          /**

7106          * Compute encryption key

7107          * @param String $user_pass user password

7108          * @param String $owner_pass user password

7109          * @param String $protection protection type

7110          * @access protected

7111          * @since 2.0.000 (2008-01-02)

7112          * @author Klemen Vodopivec

7113          */
7114  		protected function _generateencryptionkey($user_pass, $owner_pass, $protection) {
7115              // Pad passwords

7116              $user_pass = substr($user_pass.$this->padding, 0, 32);
7117              $owner_pass = substr($owner_pass.$this->padding, 0, 32);
7118              // Compute O value

7119              $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
7120              // Compute encyption key

7121              $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
7122              $this->encryption_key = substr($tmp, 0, 5);
7123              // Compute U value

7124              $this->Uvalue = $this->_Uvalue();
7125              // Compute P value

7126              $this->Pvalue = -(($protection^255) + 1);
7127          }
7128          
7129          /**

7130          * Set document protection

7131          * The permission array is composed of values taken from the following ones:

7132          * - copy: copy text and images to the clipboard

7133          * - print: print the document

7134          * - modify: modify it (except for annotations and forms)

7135          * - annot-forms: add annotations and forms 

7136          * Remark: the protection against modification is for people who have the full Acrobat product.

7137          * 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.

7138          * 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.

7139          * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)

7140          * @param String $user_pass user password. Empty by default.

7141          * @param String $owner_pass owner password. If not specified, a random value is used.

7142          * @access public

7143          * @since 2.0.000 (2008-01-02)

7144          * @author Klemen Vodopivec

7145          */
7146  		public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
7147              $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
7148              $protection = 192;
7149              foreach ($permissions as $permission) {
7150                  if (!isset($options[$permission])) {
7151                      $this->Error('Incorrect permission: '.$permission);
7152                  }
7153                  $protection += $options[$permission];
7154              }
7155              if ($owner_pass === null) {
7156                  $owner_pass = uniqid(rand());
7157              }
7158              $this->encrypted = true;
7159              $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
7160          }
7161          
7162          // END OF ENCRYPTION FUNCTIONS -------------------------

7163          
7164          // START TRANSFORMATIONS SECTION -----------------------

7165          
7166          /**

7167          * Starts a 2D tranformation saving current graphic state.

7168          * This function must be called before scaling, mirroring, translation, rotation and skewing.

7169          * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.

7170          * @access public

7171          * @since 2.1.000 (2008-01-07)

7172          * @see StartTransform(), StopTransform()

7173          */
7174  		public function StartTransform() {
7175              $this->_out('q');
7176              $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
7177          }
7178          
7179          /**

7180          * Stops a 2D tranformation restoring previous graphic state.

7181          * This function must be called after scaling, mirroring, translation, rotation and skewing.

7182          * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.

7183          * @access public

7184          * @since 2.1.000 (2008-01-07)

7185          * @see StartTransform(), StopTransform()

7186          */
7187  		public function StopTransform() {
7188              $this->_out('Q');
7189              if (isset($this->transfmatrix)) {
7190                  array_pop($this->transfmatrix);
7191              }
7192              array_pop($this->transfmrk[$this->page]);
7193          }
7194          /**

7195          * Horizontal Scaling.

7196          * @param float $s_x scaling factor for width as percent. 0 is not allowed.

7197          * @param int $x abscissa of the scaling center. Default is current x position

7198          * @param int $y ordinate of the scaling center. Default is current y position

7199          * @access public

7200          * @since 2.1.000 (2008-01-07)

7201          * @see StartTransform(), StopTransform()

7202          */
7203  		public function ScaleX($s_x, $x='', $y='') {
7204              $this->Scale($s_x, 100, $x, $y);
7205          }
7206          
7207          /**

7208          * Vertical Scaling.

7209          * @param float $s_y scaling factor for height as percent. 0 is not allowed.

7210          * @param int $x abscissa of the scaling center. Default is current x position

7211          * @param int $y ordinate of the scaling center. Default is current y position

7212          * @access public

7213          * @since 2.1.000 (2008-01-07)

7214          * @see StartTransform(), StopTransform()

7215          */
7216  		public function ScaleY($s_y, $x='', $y='') {
7217              $this->Scale(100, $s_y, $x, $y);
7218          }
7219          
7220          /**

7221          * Vertical and horizontal proportional Scaling.

7222          * @param float $s scaling factor for width and height as percent. 0 is not allowed.

7223          * @param int $x abscissa of the scaling center. Default is current x position

7224          * @param int $y ordinate of the scaling center. Default is current y position

7225          * @access public

7226          * @since 2.1.000 (2008-01-07)

7227          * @see StartTransform(), StopTransform()

7228          */
7229  		public function ScaleXY($s, $x='', $y='') {
7230              $this->Scale($s, $s, $x, $y);
7231          }
7232          
7233          /**

7234          * Vertical and horizontal non-proportional Scaling.

7235          * @param float $s_x scaling factor for width as percent. 0 is not allowed.

7236          * @param float $s_y scaling factor for height as percent. 0 is not allowed.

7237          * @param int $x abscissa of the scaling center. Default is current x position

7238          * @param int $y ordinate of the scaling center. Default is current y position

7239          * @access public

7240          * @since 2.1.000 (2008-01-07)

7241          * @see StartTransform(), StopTransform()

7242          */
7243  		public function Scale($s_x, $s_y, $x='', $y='') {
7244              if ($x === '') {
7245                  $x = $this->x;
7246              }
7247              if ($y === '') {
7248                  $y = $this->y;
7249              }
7250              if ($this->rtl) {
7251                  $x = $this->w - $x;
7252              }
7253              if (($s_x == 0) OR ($s_y == 0)) {
7254                  $this->Error('Please do not use values equal to zero for scaling');
7255              }
7256              $y = ($this->h - $y) * $this->k;
7257              $x *= $this->k;
7258              //calculate elements of transformation matrix

7259              $s_x /= 100;
7260              $s_y /= 100;
7261              $tm[0] = $s_x;
7262              $tm[1] = 0;
7263              $tm[2] = 0;
7264              $tm[3] = $s_y;
7265              $tm[4] = $x * (1 - $s_x);
7266              $tm[5] = $y * (1 - $s_y);
7267              //scale the coordinate system

7268              $this->Transform($tm);
7269          }
7270          
7271          /**

7272          * Horizontal Mirroring.

7273          * @param int $x abscissa of the point. Default is current x position

7274          * @access public

7275          * @since 2.1.000 (2008-01-07)

7276          * @see StartTransform(), StopTransform()

7277          */
7278  		public function MirrorH($x='') {
7279              $this->Scale(-100, 100, $x);
7280          }
7281          
7282          /**

7283          * Verical Mirroring.

7284          * @param int $y ordinate of the point. Default is current y position

7285          * @access public

7286          * @since 2.1.000 (2008-01-07)

7287          * @see StartTransform(), StopTransform()

7288          */
7289  		public function MirrorV($y='') {
7290              $this->Scale(100, -100, '', $y);
7291          }
7292          
7293          /**

7294          * Point reflection mirroring.

7295          * @param int $x abscissa of the point. Default is current x position

7296          * @param int $y ordinate of the point. Default is current y position

7297          * @access public

7298          * @since 2.1.000 (2008-01-07)

7299          * @see StartTransform(), StopTransform()

7300          */
7301  		public function MirrorP($x='',$y='') {
7302              $this->Scale(-100, -100, $x, $y);
7303          }
7304          
7305          /**

7306          * Reflection against a straight line through point (x, y) with the gradient angle (angle).

7307          * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).

7308          * @param int $x abscissa of the point. Default is current x position

7309          * @param int $y ordinate of the point. Default is current y position

7310          * @access public

7311          * @since 2.1.000 (2008-01-07)

7312          * @see StartTransform(), StopTransform()

7313          */
7314  		public function MirrorL($angle=0, $x='',$y='') {
7315              $this->Scale(-100, 100, $x, $y);
7316              $this->Rotate(-2*($angle-90), $x, $y);
7317          }
7318          
7319          /**

7320          * Translate graphic object horizontally.

7321          * @param int $t_x movement to the right (or left for RTL)

7322          * @access public

7323          * @since 2.1.000 (2008-01-07)

7324          * @see StartTransform(), StopTransform()

7325          */
7326  		public function TranslateX($t_x) {
7327              $this->Translate($t_x, 0);
7328          }
7329          
7330          /**

7331          * Translate graphic object vertically.

7332          * @param int $t_y movement to the bottom

7333          * @access public

7334          * @since 2.1.000 (2008-01-07)

7335          * @see StartTransform(), StopTransform()

7336          */
7337  		public function TranslateY($t_y) {
7338              $this->Translate(0, $t_y);
7339          }
7340          
7341          /**

7342          * Translate graphic object horizontally and vertically.

7343          * @param int $t_x movement to the right

7344          * @param int $t_y movement to the bottom

7345          * @access public

7346          * @since 2.1.000 (2008-01-07)

7347          * @see StartTransform(), StopTransform()

7348          */
7349  		public function Translate($t_x, $t_y) {
7350              if ($this->rtl) {
7351                  $t_x = -$t_x;
7352              }
7353              //calculate elements of transformation matrix

7354              $tm[0] = 1;
7355              $tm[1] = 0;
7356              $tm[2] = 0;
7357              $tm[3] = 1;
7358              $tm[4] = $t_x * $this->k;
7359              $tm[5] = -$t_y * $this->k;
7360              //translate the coordinate system

7361              $this->Transform($tm);
7362          }
7363          
7364          /**

7365          * Rotate object.

7366          * @param float $angle angle in degrees for counter-clockwise rotation

7367          * @param int $x abscissa of the rotation center. Default is current x position

7368          * @param int $y ordinate of the rotation center. Default is current y position

7369          * @access public

7370          * @since 2.1.000 (2008-01-07)

7371          * @see StartTransform(), StopTransform()

7372          */
7373  		public function Rotate($angle, $x='', $y='') {
7374              if ($x === '') {
7375                  $x = $this->x;
7376              }
7377              if ($y === '') {
7378                  $y = $this->y;
7379              }
7380              if ($this->rtl) {
7381                  $x = $this->w - $x;
7382                  $angle = -$angle;
7383              }
7384              $y = ($this->h - $y) * $this->k;
7385              $x *= $this->k;
7386              //calculate elements of transformation matrix

7387              $tm[0] = cos(deg2rad($angle));
7388              $tm[1] = sin(deg2rad($angle));
7389              $tm[2] = -$tm[1];
7390              $tm[3] = $tm[0];
7391              $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
7392              $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
7393              //rotate the coordinate system around ($x,$y)

7394              $this->Transform($tm);
7395          }
7396          
7397          /**

7398          * Skew horizontally.

7399          * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)

7400          * @param int $x abscissa of the skewing center. default is current x position

7401          * @param int $y ordinate of the skewing center. default is current y position

7402          * @access public

7403          * @since 2.1.000 (2008-01-07)

7404          * @see StartTransform(), StopTransform()

7405          */
7406  		public function SkewX($angle_x, $x='', $y='') {
7407              $this->Skew($angle_x, 0, $x, $y);
7408          }
7409          
7410          /**

7411          * Skew vertically.

7412          * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)

7413          * @param int $x abscissa of the skewing center. default is current x position

7414          * @param int $y ordinate of the skewing center. default is current y position

7415          * @access public

7416          * @since 2.1.000 (2008-01-07)

7417          * @see StartTransform(), StopTransform()

7418          */
7419  		public function SkewY($angle_y, $x='', $y='') {
7420              $this->Skew(0, $angle_y, $x, $y);
7421          }
7422          
7423          /**

7424          * Skew.

7425          * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)

7426          * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)

7427          * @param int $x abscissa of the skewing center. default is current x position

7428          * @param int $y ordinate of the skewing center. default is current y position

7429          * @access public

7430          * @since 2.1.000 (2008-01-07)

7431          * @see StartTransform(), StopTransform()

7432          */
7433  		public function Skew($angle_x, $angle_y, $x='', $y='') {
7434              if ($x === '') {
7435                  $x = $this->x;
7436              }
7437              if ($y === '') {
7438                  $y = $this->y;
7439              }
7440              if ($this->rtl) {
7441                  $x = $this->w - $x;
7442                  $angle_x = -$angle_x;
7443              }
7444              if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
7445                  $this->Error('Please use values between -90 and +90 degrees for Skewing.');
7446              }
7447              $x *= $this->k;
7448              $y = ($this->h - $y) * $this->k;
7449              //calculate elements of transformation matrix

7450              $tm[0] = 1;
7451              $tm[1] = tan(deg2rad($angle_y));
7452              $tm[2] = tan(deg2rad($angle_x));
7453              $tm[3] = 1;
7454              $tm[4] = -$tm[2] * $y;
7455              $tm[5] = -$tm[1] * $x;
7456              //skew the coordinate system

7457              $this->Transform($tm);
7458          }
7459          
7460          /**

7461          * Apply graphic transformations.

7462          * @access protected

7463          * @since 2.1.000 (2008-01-07)

7464          * @see StartTransform(), StopTransform()

7465          */
7466  		protected function Transform($tm) {
7467              $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
7468              // store transformation matrix

7469              $this->transfmatrix[] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
7470              // update tranformation mark

7471              if (end($this->transfmrk[$this->page]) !== false) {
7472                  $key = key($this->transfmrk[$this->page]);
7473                  $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
7474              }
7475          }
7476          
7477          // END TRANSFORMATIONS SECTION -------------------------

7478          
7479          
7480          // START GRAPHIC FUNCTIONS SECTION ---------------------

7481          // The following section is based on the code provided by David Hernandez Sanz

7482          
7483          /**

7484          * 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.

7485          * @param float $width The width.

7486          * @access public

7487          * @since 1.0

7488          * @see Line(), Rect(), Cell(), MultiCell()

7489          */
7490  		public function SetLineWidth($width) {
7491              //Set line width

7492              $this->LineWidth = $width;
7493              $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
7494              $this->_out($this->linestyleWidth);
7495          }
7496          
7497          /**

7498          * Returns the current the line width.

7499          * @return int Line width 

7500          * @access public

7501          * @since 2.1.000 (2008-01-07)

7502          * @see Line(), SetLineWidth()

7503          */
7504  		public function GetLineWidth() {
7505              return $this->LineWidth;
7506          }
7507          
7508          /**

7509          * Set line style.

7510          * @param array $style Line style. Array with keys among the following:

7511          * <ul>

7512          *     <li>width (float): Width of the line in user units.</li>

7513          *     <li>cap (string): Type of cap to put on the line. Possible values are:

7514          * butt, round, square. The difference between "square" and "butt" is that

7515          * "square" projects a flat end past the end of the line.</li>

7516          *     <li>join (string): Type of join. Possible values are: miter, round,

7517          * bevel.</li>

7518          *     <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with

7519          * series of length values, which are the lengths of the on and off dashes.

7520          * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,

7521          * 1 off, 2 on, 1 off, ...</li>

7522          *     <li>phase (integer): Modifier on the dash pattern which is used to shift

7523          * the point at which the pattern starts.</li>

7524          *     <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>

7525          * </ul>

7526          * @access public

7527          * @since 2.1.000 (2008-01-08)

7528          */
7529  		public function SetLineStyle($style) {
7530              extract($style);
7531              if (isset($width)) {
7532                  $width_prev = $this->LineWidth;
7533                  $this->SetLineWidth($width);
7534                  $this->LineWidth = $width_prev;
7535              }
7536              if (isset($cap)) {
7537                  $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
7538                  if (isset($ca[$cap])) {
7539                      $this->linestyleCap = $ca[$cap].' J';
7540                      $this->_out($this->linestyleCap);
7541                  }
7542              }
7543              if (isset($join)) {
7544                  $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
7545                  if (isset($ja[$join])) {
7546                      $this->linestyleJoin = $ja[$join].' j';
7547                      $this->_out($this->linestyleJoin);
7548                  }
7549              }
7550              if (isset($dash)) {
7551                  $dash_string = '';
7552                  if ($dash) {
7553                      if (ereg('^.+,', $dash)) {
7554                          $tab = explode(',', $dash);
7555                      } else {
7556                          $tab = array($dash);
7557                      }
7558                      $dash_string = '';
7559                      foreach ($tab as $i => $v) {
7560                          if ($i) {
7561                              $dash_string .= ' ';
7562                          }
7563                          $dash_string .= sprintf("%.2F", $v);
7564                      }
7565                  }
7566                  if (!isset($phase) OR !$dash) {
7567                      $phase = 0;
7568                  }
7569                  $this->linestyleDash = sprintf("[%s] %.2F d", $dash_string, $phase);
7570                  $this->_out($this->linestyleDash);
7571              }
7572              if (isset($color)) {
7573                  $this->SetDrawColorArray($color);
7574              }
7575          }
7576          
7577          /*

7578          * Set a draw point.

7579          * @param float $x Abscissa of point.

7580          * @param float $y Ordinate of point.

7581          * @access protected

7582          * @since 2.1.000 (2008-01-08)

7583          */
7584  		protected function _outPoint($x, $y) {
7585              if ($this->rtl) {
7586                  $x = $this->w - $x;
7587              }
7588              $this->_out(sprintf("%.2F %.2F m", $x * $this->k, ($this->h - $y) * $this->k));
7589          }
7590          
7591          /*

7592          * Draws a line from last draw point.

7593          * @param float $x Abscissa of end point.

7594          * @param float $y Ordinate of end point.

7595          * @access protected

7596          * @since 2.1.000 (2008-01-08)

7597          */
7598  		protected function _outLine($x, $y) {
7599              if ($this->rtl) {
7600                  $x = $this->w - $x;
7601              }
7602              $this->_out(sprintf("%.2F %.2F l", $x * $this->k, ($this->h - $y) * $this->k));
7603          }
7604          
7605          /**

7606          * Draws a rectangle.

7607          * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).

7608          * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).

7609          * @param float $w Width.

7610          * @param float $h Height.

7611          * @param string $op options

7612          * @access protected

7613          * @since 2.1.000 (2008-01-08)

7614          */
7615  		protected function _outRect($x, $y, $w, $h, $op) {
7616              if ($this->rtl) {
7617                  $x = $this->w - $x - $w;
7618              }
7619              $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
7620          }
7621          
7622          /*

7623          * Draws a Bezier curve from last draw point.

7624          * The Bezier curve is a tangent to the line between the control points at either end of the curve.

7625          * @param float $x1 Abscissa of control point 1.

7626          * @param float $y1 Ordinate of control point 1.

7627          * @param float $x2 Abscissa of control point 2.

7628          * @param float $y2 Ordinate of control point 2.

7629          * @param float $x3 Abscissa of end point.

7630          * @param float $y3 Ordinate of end point.

7631          * @access protected

7632          * @since 2.1.000 (2008-01-08)

7633          */
7634  		protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
7635              if ($this->rtl) {
7636                  $x1 = $this->w - $x1;
7637                  $x2 = $this->w - $x2;
7638                  $x3 = $this->w - $x3;
7639              }
7640              $this->_out(sprintf("%.2F %.2F %.2F %.2F %.2F %.2F c", $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
7641          }
7642          
7643          /**

7644          * Draws a line between two points.

7645          * @param float $x1 Abscissa of first point.

7646          * @param float $y1 Ordinate of first point.

7647          * @param float $x2 Abscissa of second point.

7648          * @param float $y2 Ordinate of second point.

7649          * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

7650          * @access public

7651          * @since 1.0

7652          * @see SetLineWidth(), SetDrawColor(), SetLineStyle()

7653          */
7654  		public function Line($x1, $y1, $x2, $y2, $style=array()) {
7655              if ($style) {
7656                  $this->SetLineStyle($style);
7657              }
7658              $this->_outPoint($x1, $y1);
7659              $this->_outLine($x2, $y2);
7660              $this->_out(' S');
7661          }
7662          
7663          /**

7664          * Draws a rectangle.

7665          * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).

7666          * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).

7667          * @param float $w Width.

7668          * @param float $h Height.

7669          * @param string $style Style of rendering. Possible values are:

7670          * <ul>

7671          *     <li>D or empty string: Draw (default).</li>

7672          *     <li>F: Fill.</li>

7673          *     <li>DF or FD: Draw and fill.</li>

7674          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

7675          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

7676          * </ul>

7677          * @param array $border_style Border style of rectangle. Array with keys among the following:

7678          * <ul>

7679          *     <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>

7680          *     <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>

7681          * </ul>

7682          * If a key is not present or is null, not draws the border. Default value: default line style (empty array).

7683          * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

7684          * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).

7685          * @access public

7686          * @since 1.0

7687          * @see SetLineStyle()

7688          */
7689  		public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
7690              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7691                  $this->SetFillColorArray($fill_color);
7692              }
7693              switch ($style) {
7694                  case 'F': {
7695                      $op = 'f';
7696                      $border_style = array();
7697                      $this->_outRect($x, $y, $w, $h, $op);
7698                      break;
7699                  }
7700                  case 'DF':
7701                  case 'FD': {
7702                      if ((!$border_style) OR (isset($border_style['all']))) {
7703                          $op = 'B';
7704                          if (isset($border_style['all'])) {
7705                              $this->SetLineStyle($border_style['all']);
7706                              $border_style = array();
7707                          }
7708                      } else {
7709                          $op = 'f';
7710                      }
7711                      $this->_outRect($x, $y, $w, $h, $op);
7712                      break;
7713                  }
7714                  case 'CNZ': {
7715                      $op = 'W n';
7716                      $this->_outRect($x, $y, $w, $h, $op);
7717                      break;
7718                  }
7719                  case 'CEO': {
7720                      $op = 'W* n';
7721                      $this->_outRect($x, $y, $w, $h, $op);
7722                      break;
7723                  }
7724                  default: {
7725                      $op = 'S';
7726                      if ((!$border_style) OR (isset($border_style['all']))) {
7727                          if (isset($border_style['all']) AND $border_style['all']) {
7728                              $this->SetLineStyle($border_style['all']);
7729                              $border_style = array();
7730                          }
7731                          $this->_outRect($x, $y, $w, $h, $op);
7732                      }
7733                      break;
7734                  }
7735              }
7736              if ($border_style) {
7737                  $border_style2 = array();
7738                  foreach ($border_style as $line => $value) {
7739                      $lenght = strlen($line);
7740                      for ($i = 0; $i < $lenght; ++$i) {
7741                          $border_style2[$line[$i]] = $value;
7742                      }
7743                  }
7744                  $border_style = $border_style2;
7745                  if (isset($border_style['L']) AND $border_style['L']) {
7746                      $this->Line($x, $y, $x, $y + $h, $border_style['L']);
7747                  }
7748                  if (isset($border_style['T']) AND $border_style['T']) {
7749                      $this->Line($x, $y, $x + $w, $y, $border_style['T']);
7750                  }
7751                  if (isset($border_style['R']) AND $border_style['R']) {
7752                      $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
7753                  }
7754                  if (isset($border_style['B']) AND $border_style['B']) {
7755                      $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
7756                  }
7757              }
7758          }
7759          
7760          
7761          /**

7762          * Draws a Bezier curve.

7763          * The Bezier curve is a tangent to the line between the control points at

7764          * either end of the curve.

7765          * @param float $x0 Abscissa of start point.

7766          * @param float $y0 Ordinate of start point.

7767          * @param float $x1 Abscissa of control point 1.

7768          * @param float $y1 Ordinate of control point 1.

7769          * @param float $x2 Abscissa of control point 2.

7770          * @param float $y2 Ordinate of control point 2.

7771          * @param float $x3 Abscissa of end point.

7772          * @param float $y3 Ordinate of end point.

7773          * @param string $style Style of rendering. Possible values are:

7774          * <ul>

7775          *     <li>D or empty string: Draw (default).</li>

7776          *     <li>F: Fill.</li>

7777          *     <li>DF or FD: Draw and fill.</li>

7778          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

7779          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

7780          * </ul>

7781          * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

7782          * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).

7783          * @access public

7784          * @see SetLineStyle()

7785          * @since 2.1.000 (2008-01-08)

7786          */
7787  		public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
7788              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7789                  $this->SetFillColorArray($fill_color);
7790              }
7791              switch ($style) {
7792                  case 'F': {
7793                      $op = 'f';
7794                      $line_style = array();
7795                      break;
7796                  }
7797                  case 'FD': 
7798                  case 'DF': {
7799                      $op = 'B';
7800                      break;
7801                  }
7802                  case 'CNZ': {
7803                      $op = 'W n';
7804                      break;
7805                  }
7806                  case 'CEO': {
7807                      $op = 'W* n';
7808                      break;
7809                  }
7810                  default: {
7811                      $op = 'S';
7812                      break;
7813                  }
7814              }
7815              if ($line_style) {
7816                  $this->SetLineStyle($line_style);
7817              }
7818              $this->_outPoint($x0, $y0);
7819              $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7820              $this->_out($op);
7821          }
7822          
7823          /**

7824          * Draws a poly-Bezier curve.

7825          * Each Bezier curve segment is a tangent to the line between the control points at

7826          * either end of the curve.

7827          * @param float $x0 Abscissa of start point.

7828          * @param float $y0 Ordinate of start point.

7829          * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).

7830          * @param string $style Style of rendering. Possible values are:

7831          * <ul>

7832          *     <li>D or empty string: Draw (default).</li>

7833          *     <li>F: Fill.</li>

7834          *     <li>DF or FD: Draw and fill.</li>

7835          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

7836          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

7837          * </ul>

7838          * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

7839          * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).

7840          * @access public

7841          * @see SetLineStyle()

7842          * @since 3.0008 (2008-05-12)

7843          */
7844  		public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
7845              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7846                  $this->SetFillColorArray($fill_color);
7847              }
7848              switch ($style) {
7849                  case 'F': {
7850                      $op = 'f';
7851                      $line_style = array();
7852                      break;
7853                  }
7854                  case 'FD':
7855                  case 'DF': {
7856                      $op = 'B';
7857                      break;
7858                  }
7859                  case 'CNZ': {
7860                      $op = 'W n';
7861                      break;
7862                  }
7863                  case 'CEO': {
7864                      $op = 'W* n';
7865                      break;
7866                  }
7867                  default: {
7868                      $op = 'S';
7869                      break;
7870                  }
7871              }
7872              if ($line_style) {
7873                  $this->SetLineStyle($line_style);
7874              }
7875              $this->_outPoint($x0, $y0);
7876              foreach ($segments as $segment) {
7877                  list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
7878                  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7879              }    
7880              $this->_out($op);
7881          }
7882          
7883          /**

7884          * Draws an ellipse.

7885          * An ellipse is formed from n Bezier curves.

7886          * @param float $x0 Abscissa of center point.

7887          * @param float $y0 Ordinate of center point.

7888          * @param float $rx Horizontal radius.

7889          * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.

7890          * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.

7891          * @param float $astart: Angle start of draw line. Default value: 0.

7892          * @param float $afinish: Angle finish of draw line. Default value: 360.

7893          * @param string $style Style of rendering. Possible values are:

7894          * <ul>

7895          *     <li>D or empty string: Draw (default).</li>

7896          *     <li>F: Fill.</li>

7897          *     <li>DF or FD: Draw and fill.</li>

7898          *     <li>C: Draw close.</li>

7899          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

7900          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

7901          * </ul>

7902          * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

7903          * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).

7904          * @param integer $nc Number of curves used in ellipse. Default value: 8.

7905          * @access public

7906          * @since 2.1.000 (2008-01-08)

7907          */
7908  		public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
7909              if ($angle) {
7910                  $this->StartTransform();
7911                  $this->Rotate($angle, $x0, $y0);
7912                  $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
7913                  $this->StopTransform();
7914                  return;
7915              }
7916              if ($rx) {
7917                  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7918                      $this->SetFillColorArray($fill_color);
7919                  }
7920                  switch ($style) {
7921                      case 'F': {
7922                          $op = 'f';
7923                          $line_style = array();
7924                          break;
7925                      }
7926                      case 'FD': 
7927                      case 'DF': {
7928                          $op = 'B';
7929                          break;
7930                      }
7931                      case 'C': {
7932                          $op = 's'; // Small 's' signifies closing the path as well

7933                          break;
7934                      }
7935                      case 'CNZ': {
7936                          $op = 'W n';
7937                          break;
7938                      }
7939                      case 'CEO': {
7940                          $op = 'W* n';
7941                          break;
7942                      }
7943                      default: {
7944                          $op = 'S';
7945                          break;
7946                      }
7947                  }
7948                  if ($line_style) {
7949                      $this->SetLineStyle($line_style);
7950                  }
7951                  if (!$ry) {
7952                      $ry = $rx;
7953                  }
7954                  $rx *= $this->k;
7955                  $ry *= $this->k;
7956                  if ($nc < 2) {
7957                      $nc = 2;
7958                  }
7959                  $astart = deg2rad((float) $astart);
7960                  $afinish = deg2rad((float) $afinish);
7961                  $total_angle = $afinish - $astart;
7962                  $dt = $total_angle / $nc;
7963                  $dtm = $dt / 3;
7964                  $x0 *= $this->k;
7965                  $y0 = ($this->h - $y0) * $this->k;
7966                  $t1 = $astart;
7967                  $a0 = $x0 + ($rx * cos($t1));
7968                  $b0 = $y0 + ($ry * sin($t1));
7969                  $c0 = -$rx * sin($t1);
7970                  $d0 = $ry * cos($t1);
7971                  $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
7972                  for ($i = 1; $i <= $nc; ++$i) {
7973                      // Draw this bit of the total curve

7974                      $t1 = ($i * $dt) + $astart;
7975                      $a1 = $x0 + ($rx * cos($t1));
7976                      $b1 = $y0 + ($ry * sin($t1));
7977                      $c1 = -$rx * sin($t1);
7978                      $d1 = $ry * cos($t1);
7979                      $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k));
7980                      $a0 = $a1;
7981                      $b0 = $b1;
7982                      $c0 = $c1;
7983                      $d0 = $d1;
7984                  }
7985                  $this->_out($op);
7986              }
7987          }
7988          
7989          /**

7990          * Draws a circle.

7991          * A circle is formed from n Bezier curves.

7992          * @param float $x0 Abscissa of center point.

7993          * @param float $y0 Ordinate of center point.

7994          * @param float $r Radius.

7995          * @param float $astart: Angle start of draw line. Default value: 0.

7996          * @param float $afinish: Angle finish of draw line. Default value: 360.

7997          * @param string $style Style of rendering. Possible values are:

7998          * <ul>

7999          *     <li>D or empty string: Draw (default).</li>

8000          *     <li>F: Fill.</li>

8001          *     <li>DF or FD: Draw and fill.</li>

8002          *     <li>C: Draw close.</li>

8003          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

8004          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

8005          * </ul>

8006          * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

8007          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).

8008          * @param integer $nc Number of curves used in circle. Default value: 8.

8009          * @access public

8010          * @since 2.1.000 (2008-01-08)

8011          */
8012  		public function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
8013              $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
8014          }
8015          
8016          /**

8017          * Draws a polygon.

8018          * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))

8019          * @param string $style Style of rendering. Possible values are:

8020          * <ul>

8021          *     <li>D or empty string: Draw (default).</li>

8022          *     <li>F: Fill.</li>

8023          *     <li>DF or FD: Draw and fill.</li>

8024          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

8025          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

8026          * </ul>

8027          * @param array $line_style Line style of polygon. Array with keys among the following:

8028          * <ul>

8029          *     <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>

8030          *     <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>

8031          * </ul>

8032          * If a key is not present or is null, not draws the line. Default value is default line style (empty array).

8033          * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).

8034          * @access public

8035          * @since 2.1.000 (2008-01-08)

8036          */
8037  		public function Polygon($p, $style='', $line_style=array(), $fill_color=array()) {
8038              $np = count($p) / 2;
8039              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8040                  $this->SetFillColorArray($fill_color);
8041              }
8042              switch ($style) {
8043                  case 'F': {
8044                      $line_style = array();
8045                      $op = 'f';
8046                      break;
8047                  }
8048                  case 'FD': 
8049                  case 'DF': {
8050                      $op = 'B';
8051                      break;
8052                  }
8053                  case 'CNZ': {
8054                      $op = 'W n';
8055                      break;
8056                  }
8057                  case 'CEO': {
8058                      $op = 'W* n';
8059                      break;
8060                  }                
8061                  default: {
8062                      $op = 'S';
8063                      break;
8064                  }
8065              }
8066              $draw = true;
8067              if ($line_style) {
8068                  if (isset($line_style['all'])) {
8069                      $this->SetLineStyle($line_style['all']);
8070                  } else { // 0 .. (np - 1), op = {B, S}
8071                      $draw = false;
8072                      if ('B' == $op) {
8073                          $op = 'f';
8074                          $this->_outPoint($p[0], $p[1]);
8075                          for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8076                              $this->_outLine($p[$i], $p[$i + 1]);
8077                          }
8078                          $this->_outLine($p[0], $p[1]);
8079                          $this->_out($op);
8080                      }
8081                      $p[($np * 2)] = $p[0];
8082                      $p[(($np * 2) + 1)] = $p[1];
8083                      for ($i = 0; $i < $np; ++$i) {
8084                          if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
8085                              $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
8086                          }
8087                      }
8088                  }
8089              }
8090              if ($draw) {
8091                  $this->_outPoint($p[0], $p[1]);
8092                  for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8093                      $this->_outLine($p[$i], $p[$i + 1]);
8094                  }
8095                  $this->_outLine($p[0], $p[1]);
8096                  $this->_out($op);
8097              }
8098          }
8099          
8100          /**

8101          * Draws a regular polygon.

8102          * @param float $x0 Abscissa of center point.

8103          * @param float $y0 Ordinate of center point.

8104          * @param float $r: Radius of inscribed circle.

8105          * @param integer $ns Number of sides.

8106          * @param float $angle Angle oriented (anti-clockwise). Default value: 0.

8107          * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.

8108          * @param string $style Style of rendering. Possible values are:

8109          * <ul>

8110          *     <li>D or empty string: Draw (default).</li>

8111          *     <li>F: Fill.</li>

8112          *     <li>DF or FD: Draw and fill.</li>

8113          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

8114          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

8115          * </ul>

8116          * @param array $line_style Line style of polygon sides. Array with keys among the following:

8117          * <ul>

8118          *     <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>

8119          *     <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>

8120          * </ul>

8121          * If a key is not present or is null, not draws the side. Default value is default line style (empty array).

8122          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).

8123          * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:

8124          * <ul>

8125          *     <li>D or empty string: Draw (default).</li>

8126          *     <li>F: Fill.</li>

8127          *     <li>DF or FD: Draw and fill.</li>

8128          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

8129          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

8130          * </ul>

8131          * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

8132          * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).

8133          * @access public

8134          * @since 2.1.000 (2008-01-08)

8135          */
8136  		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()) {
8137              if (3 > $ns) {
8138                  $ns = 3;
8139              }
8140              if ($draw_circle) {
8141                  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8142              }
8143              $p = array();
8144              for ($i = 0; $i < $ns; ++$i) {
8145                  $a = $angle + ($i * 360 / $ns);
8146                  $a_rad = deg2rad((float) $a);
8147                  $p[] = $x0 + ($r * sin($a_rad));
8148                  $p[] = $y0 + ($r * cos($a_rad));
8149              }
8150              $this->Polygon($p, $style, $line_style, $fill_color);
8151          }
8152          
8153          /**

8154          * Draws a star polygon

8155          * @param float $x0 Abscissa of center point.

8156          * @param float $y0 Ordinate of center point.

8157          * @param float $r Radius of inscribed circle.

8158          * @param integer $nv Number of vertices.

8159          * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).

8160          * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.

8161          * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.

8162          * @param string $style Style of rendering. Possible values are:

8163          * <ul>

8164          *     <li>D or empty string: Draw (default).</li>

8165          *     <li>F: Fill.</li>

8166          *     <li>DF or FD: Draw and fill.</li>

8167          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

8168          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

8169          * </ul>

8170          * @param array $line_style Line style of polygon sides. Array with keys among the following:

8171          * <ul>

8172          *     <li>all: Line style of all sides. Array like for

8173          * {@link SetLineStyle SetLineStyle}.</li>

8174          *     <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>

8175          * </ul>

8176          * If a key is not present or is null, not draws the side. Default value is default line style (empty array).

8177          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).

8178          * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:

8179          * <ul>

8180          *     <li>D or empty string: Draw (default).</li>

8181          *     <li>F: Fill.</li>

8182          *     <li>DF or FD: Draw and fill.</li>

8183          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

8184          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

8185          * </ul>

8186          * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

8187          * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).

8188          * @access public

8189          * @since 2.1.000 (2008-01-08)

8190          */
8191  		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()) {
8192              if (2 > $nv) {
8193                  $nv = 2;
8194              }
8195              if ($draw_circle) {
8196                  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8197              }
8198              $p2 = array();
8199              $visited = array();
8200              for ($i = 0; $i < $nv; ++$i) {
8201                  $a = $angle + ($i * 360 / $nv);
8202                  $a_rad = deg2rad((float) $a);
8203                  $p2[] = $x0 + ($r * sin($a_rad));
8204                  $p2[] = $y0 + ($r * cos($a_rad));
8205                  $visited[] = false;
8206              }
8207              $p = array();
8208              $i = 0;
8209              do {
8210                  $p[] = $p2[$i * 2];
8211                  $p[] = $p2[($i * 2) + 1];
8212                  $visited[$i] = true;
8213                  $i += $ng;
8214                  $i %= $nv;
8215              } while (!$visited[$i]);
8216              $this->Polygon($p, $style, $line_style, $fill_color);
8217          }
8218          
8219          /**

8220          * Draws a rounded rectangle.

8221          * @param float $x Abscissa of upper-left corner.

8222          * @param float $y Ordinate of upper-left corner.

8223          * @param float $w Width.

8224          * @param float $h Height.

8225          * @param float $r Radius of the rounded corners.

8226          * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111").

8227          * @param string $style Style of rendering. Possible values are:

8228          * <ul>

8229          *     <li>D or empty string: Draw (default).</li>

8230          *     <li>F: Fill.</li>

8231          *     <li>DF or FD: Draw and fill.</li>

8232          *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>

8233          *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>

8234          * </ul>

8235          * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).

8236          * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).

8237          * @access public

8238          * @since 2.1.000 (2008-01-08)

8239          */
8240  		public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
8241              if ('0000' == $round_corner) { // Not rounded
8242                  $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
8243              } else { // Rounded
8244                  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8245                      $this->SetFillColorArray($fill_color);
8246                  }
8247                  switch ($style) {
8248                      case 'F': {
8249                          $border_style = array();
8250                          $op = 'f';
8251                          break;
8252                      }
8253                      case 'FD': 
8254                      case 'DF': {
8255                          $op = 'B';
8256                          break;
8257                      }
8258                      case 'CNZ': {
8259                          $op = 'W n';
8260                          break;
8261                      }
8262                      case 'CEO': {
8263                          $op = 'W* n';
8264                          break;
8265                      }
8266                      default: {
8267                          $op = 'S';
8268                          break;
8269                      }
8270                  }
8271                  if ($border_style) {
8272                      $this->SetLineStyle($border_style);
8273                  }
8274                  $MyArc = 4 / 3 * (sqrt(2) - 1);
8275                  $this->_outPoint($x + $r, $y);
8276                  $xc = $x + $w - $r;
8277                  $yc = $y + $r;
8278                  $this->_outLine($xc, $y);
8279                  if ($round_corner[0]) {
8280                      $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
8281                  } else {
8282                      $this->_outLine($x + $w, $y);
8283                  }
8284                  $xc = $x + $w - $r;
8285                  $yc = $y + $h - $r;
8286                  $this->_outLine($x + $w, $yc);
8287                  if ($round_corner[1]) {
8288                      $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
8289                  } else {
8290                      $this->_outLine($x + $w, $y + $h);
8291                  }
8292                  $xc = $x + $r;
8293                  $yc = $y + $h - $r;
8294                  $this->_outLine($xc, $y + $h);
8295                  if ($round_corner[2]) {
8296                      $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
8297                  } else {
8298                      $this->_outLine($x, $y + $h);
8299                  }
8300                  $xc = $x + $r;
8301                  $yc = $y + $r;
8302                  $this->_outLine($x, $yc);
8303                  if ($round_corner[3]) {
8304                      $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
8305                  } else {
8306                      $this->_outLine($x, $y);
8307                      $this->_outLine($x + $r, $y);
8308                  }
8309                  $this->_out($op);
8310              }
8311          }
8312          
8313          // END GRAPHIC FUNCTIONS SECTION -----------------------

8314          
8315          // BIDIRECTIONAL TEXT SECTION --------------------------

8316          /**

8317           * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).

8318           * @param string $str string to manipulate.

8319           * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR

8320           * @return string

8321           * @access protected

8322           * @author Nicola Asuni

8323           * @since 2.1.000 (2008-01-08)

8324          */
8325  		protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
8326              return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $str, $forcertl), $setbom);
8327          }
8328          
8329          /**

8330           * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).

8331           * @param array $ta array of characters composing the string.

8332           * @param string $str string to process

8333           * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR

8334           * @return string

8335           * @author Nicola Asuni

8336           * @access protected

8337           * @since 2.4.000 (2008-03-06)

8338          */
8339  		protected function utf8Bidi($ta, $str='', $forcertl=false) {
8340              global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
8341              // paragraph embedding level

8342              $pel = 0;
8343              // max level

8344              $maxlevel = 0;
8345              if ($this->empty_string($str)) {
8346                  // create string from array

8347                  $str = $this->UTF8ArrSubString($ta);
8348              }
8349              // check if string contains arabic text

8350              if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
8351                  $arabic = true;
8352              } else {
8353                  $arabic = false;
8354              }
8355              // check if string contains RTL text

8356              if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
8357                  return $ta;
8358              }
8359              
8360              // get number of chars

8361              $numchars = count($ta);
8362              
8363              if ($forcertl == 'R') {
8364                      $pel = 1;
8365              } elseif ($forcertl == 'L') {
8366                      $pel = 0;
8367              } else {
8368                  // P2. In each paragraph, find the first character of type L, AL, or R.

8369                  // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero.

8370                  for ($i=0; $i < $numchars; ++$i) {
8371                      $type = $unicode[$ta[$i]];
8372                      if ($type == 'L') {
8373                          $pel = 0;
8374                          break;
8375                      } elseif (($type == 'AL') OR ($type == 'R')) {
8376                          $pel = 1;
8377                          break;
8378                      }
8379                  }
8380              }
8381              
8382              // Current Embedding Level

8383              $cel = $pel;
8384              // directional override status

8385              $dos = 'N';
8386              $remember = array();
8387              // start-of-level-run

8388              $sor = $pel % 2 ? 'R' : 'L';
8389              $eor = $sor;
8390              
8391              // Array of characters data

8392              $chardata = Array();
8393              
8394              // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase.

8395              //     In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.

8396              for ($i=0; $i < $numchars; ++$i) {
8397                  if ($ta[$i] == K_RLE) {
8398                      // X2. With each RLE, compute the least greater odd embedding level.

8399                      //    a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.

8400                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.

8401                      $next_level = $cel + ($cel % 2) + 1;
8402                      if ($next_level < 62) {
8403                          $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
8404                          $cel = $next_level;
8405                          $dos = 'N';
8406                          $sor = $eor;
8407                          $eor = $cel % 2 ? 'R' : 'L';
8408                      }
8409                  } elseif ($ta[$i] == K_LRE) {
8410                      // X3. With each LRE, compute the least greater even embedding level.

8411                      //    a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.

8412                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.

8413                      $next_level = $cel + 2 - ($cel % 2);
8414                      if ( $next_level < 62 ) {
8415                          $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
8416                          $cel = $next_level;
8417                          $dos = 'N';
8418                          $sor = $eor;
8419                          $eor = $cel % 2 ? 'R' : 'L';
8420                      }
8421                  } elseif ($ta[$i] == K_RLO) {
8422                      // X4. With each RLO, compute the least greater odd embedding level.

8423                      //    a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left.

8424                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.

8425                      $next_level = $cel + ($cel % 2) + 1;
8426                      if ($next_level < 62) {
8427                          $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
8428                          $cel = $next_level;
8429                          $dos = 'R';
8430                          $sor = $eor;
8431                          $eor = $cel % 2 ? 'R' : 'L';
8432                      }
8433                  } elseif ($ta[$i] == K_LRO) {
8434                      // X5. With each LRO, compute the least greater even embedding level.

8435                      //    a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right.

8436                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.

8437                      $next_level = $cel + 2 - ($cel % 2);
8438                      if ( $next_level < 62 ) {
8439                          $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
8440                          $cel = $next_level;
8441                          $dos = 'L';
8442                          $sor = $eor;
8443                          $eor = $cel % 2 ? 'R' : 'L';
8444                      }
8445                  } elseif ($ta[$i] == K_PDF) {
8446                      // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override.

8447                      if (count($remember)) {
8448                          $last = count($remember ) - 1;
8449                          if (($remember[$last]['num'] == K_RLE) OR 
8450                                ($remember[$last]['num'] == K_LRE) OR 
8451                                ($remember[$last]['num'] == K_RLO) OR 
8452                                ($remember[$last]['num'] == K_LRO)) {
8453                              $match = array_pop($remember);
8454                              $cel = $match['cel'];
8455                              $dos = $match['dos'];
8456                              $sor = $eor;
8457                              $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
8458                          }
8459                      }
8460                  } elseif (($ta[$i] != K_RLE) AND
8461                                   ($ta[$i] != K_LRE) AND
8462                                   ($ta[$i] != K_RLO) AND
8463                                   ($ta[$i] != K_LRO) AND
8464                                   ($ta[$i] != K_PDF)) {
8465                      // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:

8466                      //    a. Set the level of the current character to the current embedding level.

8467                      //    b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.

8468                      if ($dos != 'N') {
8469                          $chardir = $dos;
8470                      } else {
8471                          $chardir = $unicode[$ta[$i]];
8472                      }
8473                      // stores string characters and other information

8474                      $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
8475                  }
8476              } // end for each char

8477              
8478              // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.

8479              // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.

8480              // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L.

8481              
8482              // 3.3.3 Resolving Weak Types

8483              // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used.

8484              // Nonspacing marks are now resolved based on the previous characters.

8485              $numchars = count($chardata);
8486              
8487              // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor.

8488              $prevlevel = -1; // track level changes

8489              $levcount = 0; // counts consecutive chars at the same level

8490              for ($i=0; $i < $numchars; ++$i) {
8491                  if ($chardata[$i]['type'] == 'NSM') {
8492                      if ($levcount) {
8493                          $chardata[$i]['type'] = $chardata[$i]['sor'];
8494                      } elseif ($i > 0) {
8495                          $chardata[$i]['type'] = $chardata[($i-1)]['type'];
8496                      }
8497                  }
8498                  if ($chardata[$i]['level'] != $prevlevel) {
8499                      $levcount = 0;
8500                  } else {
8501                      ++$levcount;
8502                  }
8503                  $prevlevel = $chardata[$i]['level'];
8504              }
8505              
8506              // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number.

8507              $prevlevel = -1;
8508              $levcount = 0;
8509              for ($i=0; $i < $numchars; ++$i) {
8510                  if ($chardata[$i]['char'] == 'EN') {
8511                      for ($j=$levcount; $j >= 0; $j--) {
8512                          if ($chardata[$j]['type'] == 'AL') {
8513                              $chardata[$i]['type'] = 'AN';
8514                          } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
8515                              break;
8516                          }
8517                      }
8518                  }
8519                  if ($chardata[$i]['level'] != $prevlevel) {
8520                      $levcount = 0;
8521                  } else {
8522                      ++$levcount;
8523                  }
8524                  $prevlevel = $chardata[$i]['level'];
8525              }
8526              
8527              // W3. Change all ALs to R.

8528              for ($i=0; $i < $numchars; ++$i) {
8529                  if ($chardata[$i]['type'] == 'AL') {
8530                      $chardata[$i]['type'] = 'R';
8531                  } 
8532              }
8533              
8534              // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type.

8535              $prevlevel = -1;
8536              $levcount = 0;
8537              for ($i=0; $i < $numchars; ++$i) {
8538                  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8539                      if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8540                          $chardata[$i]['type'] = 'EN';
8541                      } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8542                          $chardata[$i]['type'] = 'EN';
8543                      } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
8544                          $chardata[$i]['type'] = 'AN';
8545                      }
8546                  }
8547                  if ($chardata[$i]['level'] != $prevlevel) {
8548                      $levcount = 0;
8549                  } else {
8550                      ++$levcount;
8551                  }
8552                  $prevlevel = $chardata[$i]['level'];
8553              }
8554              
8555              // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.

8556              $prevlevel = -1;
8557              $levcount = 0;
8558              for ($i=0; $i < $numchars; ++$i) {
8559                  if ($chardata[$i]['type'] == 'ET') {
8560                      if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
8561                          $chardata[$i]['type'] = 'EN';
8562                      } else {
8563                          $j = $i+1;
8564                          while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
8565                              if ($chardata[$j]['type'] == 'EN') {
8566                                  $chardata[$i]['type'] = 'EN';
8567                                  break;
8568                              } elseif ($chardata[$j]['type'] != 'ET') {
8569                                  break;
8570                              }
8571                              ++$j;
8572                          }
8573                      }
8574                  }
8575                  if ($chardata[$i]['level'] != $prevlevel) {
8576                      $levcount = 0;
8577                  } else {
8578                      ++$levcount;
8579                  }
8580                  $prevlevel = $chardata[$i]['level'];
8581              }
8582              
8583              // W6. Otherwise, separators and terminators change to Other Neutral.

8584              $prevlevel = -1;
8585              $levcount = 0;
8586              for ($i=0; $i < $numchars; ++$i) {
8587                  if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
8588                      $chardata[$i]['type'] = 'ON';
8589                  }
8590                  if ($chardata[$i]['level'] != $prevlevel) {
8591                      $levcount = 0;
8592                  } else {
8593                      ++$levcount;
8594                  }
8595                  $prevlevel = $chardata[$i]['level'];
8596              }
8597              
8598              //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L.

8599              $prevlevel = -1;
8600              $levcount = 0;
8601              for ($i=0; $i < $numchars; ++$i) {
8602                  if ($chardata[$i]['char'] == 'EN') {
8603                      for ($j=$levcount; $j >= 0; $j--) {
8604                          if ($chardata[$j]['type'] == 'L') {
8605                              $chardata[$i]['type'] = 'L';
8606                          } elseif ($chardata[$j]['type'] == 'R') {
8607                              break;
8608                          }
8609                      }
8610                  }
8611                  if ($chardata[$i]['level'] != $prevlevel) {
8612                      $levcount = 0;
8613                  } else {
8614                      ++$levcount;
8615                  }
8616                  $prevlevel = $chardata[$i]['level'];
8617              }
8618              
8619              // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries.

8620              $prevlevel = -1;
8621              $levcount = 0;
8622              for ($i=0; $i < $numchars; ++$i) {
8623                  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8624                      if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8625                          $chardata[$i]['type'] = 'L';
8626                      } elseif (($chardata[$i]['type'] == 'N') AND
8627                       (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8628                       (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8629                          $chardata[$i]['type'] = 'R';
8630                      } elseif ($chardata[$i]['type'] == 'N') {
8631                          // N2. Any remaining neutrals take the embedding direction

8632                          $chardata[$i]['type'] = $chardata[$i]['sor'];
8633                      }
8634                  } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8635                      // first char

8636                      if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8637                          $chardata[$i]['type'] = 'L';
8638                      } elseif (($chardata[$i]['type'] == 'N') AND
8639                       (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
8640                       (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8641                          $chardata[$i]['type'] = 'R';
8642                      } elseif ($chardata[$i]['type'] == 'N') {
8643                          // N2. Any remaining neutrals take the embedding direction

8644                          $chardata[$i]['type'] = $chardata[$i]['sor'];
8645                      }
8646                  } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
8647                      //last char

8648                      if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
8649                          $chardata[$i]['type'] = 'L';
8650                      } elseif (($chardata[$i]['type'] == 'N') AND
8651                       (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8652                       (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
8653                          $chardata[$i]['type'] = 'R';
8654                      } elseif ($chardata[$i]['type'] == 'N') {
8655                          // N2. Any remaining neutrals take the embedding direction

8656                          $chardata[$i]['type'] = $chardata[$i]['sor'];
8657                      }
8658                  } elseif ($chardata[$i]['type'] == 'N') {
8659                      // N2. Any remaining neutrals take the embedding direction

8660                      $chardata[$i]['type'] = $chardata[$i]['sor'];
8661                  }
8662                  if ($chardata[$i]['level'] != $prevlevel) {
8663                      $levcount = 0;
8664                  } else {
8665                      ++$levcount;
8666                  }
8667                  $prevlevel = $chardata[$i]['level'];
8668              }
8669              
8670              // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels.

8671              // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.

8672              for ($i=0; $i < $numchars; ++$i) {
8673                  $odd = $chardata[$i]['level'] % 2;
8674                  if ($odd) {
8675                      if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8676                          $chardata[$i]['level'] += 1;
8677                      }
8678                  } else {
8679                      if ($chardata[$i]['type'] == 'R') {
8680                          $chardata[$i]['level'] += 1;
8681                      } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8682                          $chardata[$i]['level'] += 2;
8683                      }
8684                  }
8685                  $maxlevel = max($chardata[$i]['level'],$maxlevel);
8686              }
8687              
8688              // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:

8689              //    1. Segment separators,

8690              //    2. Paragraph separators,

8691              //    3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and

8692              //    4. Any sequence of white space characters at the end of the line.

8693              for ($i=0; $i < $numchars; ++$i) {
8694                  if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
8695                      $chardata[$i]['level'] = $pel;
8696                  } elseif ($chardata[$i]['type'] == 'WS') {
8697                      $j = $i+1;
8698                      while ($j < $numchars) {
8699                          if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
8700                              (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
8701                              $chardata[$i]['level'] = $pel;
8702                              break;
8703                          } elseif ($chardata[$j]['type'] != 'WS') {
8704                              break;
8705                          }
8706                          ++$j;
8707                      }
8708                  }
8709              }
8710              
8711              // Arabic Shaping

8712              // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run. 

8713              if ($arabic) {
8714                  $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
8715                  $alfletter = array(1570,1571,1573,1575);
8716                  $chardata2 = $chardata;
8717                  $laaletter = false;
8718                  $charAL = array();
8719                  $x = 0;
8720                  for ($i=0; $i < $numchars; ++$i) {
8721                      if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
8722                          $charAL[$x] = $chardata[$i];
8723                          $charAL[$x]['i'] = $i;
8724                          $chardata[$i]['x'] = $x;
8725                          ++$x;
8726                      }
8727                  }
8728                  $numAL = $x;
8729                  for ($i=0; $i < $numchars; ++$i) {
8730                      $thischar = $chardata[$i];
8731                      if ($i > 0) {
8732                          $prevchar = $chardata[($i-1)];
8733                      } else {
8734                          $prevchar = false;
8735                      }
8736                      if (($i+1) < $numchars) {
8737                          $nextchar = $chardata[($i+1)];
8738                      } else {
8739                          $nextchar = false;
8740                      }
8741                      if ($unicode[$thischar['char']] == 'AL') {
8742                          $x = $thischar['x'];
8743                          if ($x > 0) {
8744                              $prevchar = $charAL[($x-1)];
8745                          } else {
8746                              $prevchar = false;
8747                          }
8748                          if (($x+1) < $numAL) {
8749                              $nextchar = $charAL[($x+1)];
8750                          } else {
8751                              $nextchar = false;
8752                          }
8753                          // if laa letter

8754                          if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
8755                              $arabicarr = $laa_array;
8756                              $laaletter = true;
8757                              if ($x > 1) {
8758                                  $prevchar = $charAL[($x-2)];
8759                              } else {
8760                                  $prevchar = false;
8761                              }
8762                          } else {
8763                              $arabicarr = $unicode_arlet;
8764                              $laaletter = false;
8765                          }
8766                          if (($prevchar !== false) AND ($nextchar !== false) AND
8767                              (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8768                              (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8769                              ($prevchar['type'] == $thischar['type']) AND
8770                              ($nextchar['type'] == $thischar['type']) AND
8771                              ($nextchar['char'] != 1567)) {
8772                              if (in_array($prevchar['char'], $endedletter)) {
8773                                  if (isset($arabicarr[$thischar['char']][2])) {
8774                                      // initial

8775                                      $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8776                                  }
8777                              } else {
8778                                  if (isset($arabicarr[$thischar['char']][3])) {
8779                                      // medial

8780                                      $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
8781                                  }
8782                              }
8783                          } elseif (($nextchar !== false) AND
8784                              (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8785                              ($nextchar['type'] == $thischar['type']) AND
8786                              ($nextchar['char'] != 1567)) {
8787                              if (isset($arabicarr[$chardata[$i]['char']][2])) {
8788                                  // initial

8789                                  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8790                              }
8791                          } elseif ((($prevchar !== false) AND
8792                              (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8793                              ($prevchar['type'] == $thischar['type'])) OR
8794                              (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
8795                              // final

8796                              if (($i > 1) AND ($thischar['char'] == 1607) AND
8797                                  ($chardata[$i-1]['char'] == 1604) AND
8798                                  ($chardata[$i-2]['char'] == 1604)) {
8799                                  //Allah Word

8800                                  // mark characters to delete with false

8801                                  $chardata2[$i-2]['char'] = false;
8802                                  $chardata2[$i-1]['char'] = false; 
8803                                  $chardata2[$i]['char'] = 65010;
8804                              } else {
8805                                  if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
8806                                      if (isset($arabicarr[$thischar['char']][0])) {
8807                                          // isolated

8808                                          $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8809                                      }
8810                                  } else {
8811                                      if (isset($arabicarr[$thischar['char']][1])) {
8812                                          // final

8813                                          $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
8814                                      }
8815                                  }
8816                              }
8817                          } elseif (isset($arabicarr[$thischar['char']][0])) {
8818                              // isolated

8819                              $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8820                          }
8821                          // if laa letter

8822                          if ($laaletter) {
8823                              // mark characters to delete with false

8824                              $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
8825                          }
8826                      } // end if AL (Arabic Letter)

8827                  } // end for each char

8828                  /* 

8829                   * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594. 

8830                   * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.

8831                   */
8832                  $cw = &$this->CurrentFont['cw'];
8833                  for ($i = 0; $i < ($numchars-1); ++$i) {
8834                      if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
8835                          // check if the subtitution font is defined on current font

8836                          if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
8837                              $chardata2[$i]['char'] = false;
8838                              $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
8839                          }
8840                      }
8841                  }
8842                  // remove marked characters

8843                  foreach ($chardata2 as $key => $value) {
8844                      if ($value['char'] === false) {
8845                          unset($chardata2[$key]);
8846                      }
8847                  }
8848                  $chardata = array_values($chardata2);
8849                  $numchars = count($chardata);
8850                  unset($chardata2);
8851                  unset($arabicarr);
8852                  unset($laaletter);
8853                  unset($charAL);
8854              }
8855              
8856              // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.

8857              for ($j=$maxlevel; $j > 0; $j--) {
8858                  $ordarray = Array();
8859                  $revarr = Array();
8860                  $onlevel = false;
8861                  for ($i=0; $i < $numchars; ++$i) {
8862                      if ($chardata[$i]['level'] >= $j) {
8863                          $onlevel = true;
8864                          if (isset($unicode_mirror[$chardata[$i]['char']])) {
8865                              // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true.

8866                              $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
8867                          }
8868                          $revarr[] = $chardata[$i];
8869                      } else {
8870                          if ($onlevel) {
8871                              $revarr = array_reverse($revarr);
8872                              $ordarray = array_merge($ordarray, $revarr);
8873                              $revarr = Array();
8874                              $onlevel = false;
8875                          }
8876                          $ordarray[] = $chardata[$i];
8877                      }
8878                  }
8879                  if ($onlevel) {
8880                      $revarr = array_reverse($revarr);
8881                      $ordarray = array_merge($ordarray, $revarr);
8882                  }
8883                  $chardata = $ordarray;
8884              }
8885              
8886              $ordarray = array();
8887              for ($i=0; $i < $numchars; ++$i) {
8888                  $ordarray[] = $chardata[$i]['char'];
8889              }
8890              
8891              return $ordarray;
8892          }
8893          
8894          // END OF BIDIRECTIONAL TEXT SECTION -------------------

8895          
8896          /*

8897          * Adds a bookmark.

8898          * @param string $txt bookmark description.

8899          * @param int $level bookmark level (minimum value is 0).

8900          * @param float $y Ordinate of the boorkmark position (default = -1 = current position).

8901          * @param int $page target page number (leave empty for current page).

8902          * @access public

8903          * @author Olivier Plathey, Nicola Asuni

8904          * @since 2.1.002 (2008-02-12)

8905          */
8906  		public function Bookmark($txt, $level=0, $y=-1, $page='') {
8907              if ($level < 0) {
8908                  $level = 0;
8909              }
8910              if (isset($this->outlines[0])) {
8911                  $lastoutline = end($this->outlines);
8912                  $maxlevel = $lastoutline['l'] + 1;
8913              } else {
8914                  $maxlevel = 0;
8915              }
8916              if ($level > $maxlevel) {
8917                  $level = $maxlevel;
8918              }
8919              if ($y == -1) {
8920                  $y = $this->GetY();
8921              }
8922              if (empty($page)) {
8923                  $page = $this->PageNo();
8924              }
8925              $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
8926          }
8927          
8928          /*

8929          * Create a bookmark PDF string.

8930          * @access protected

8931          * @author Olivier Plathey, Nicola Asuni

8932          * @since 2.1.002 (2008-02-12)

8933          */
8934  		protected function _putbookmarks() {
8935              $nb = count($this->outlines);
8936              if ($nb == 0) {
8937                  return;
8938              }
8939              $lru = array();
8940              $level = 0;
8941              foreach ($this->outlines as $i => $o) {
8942                  if ($o['l'] > 0) {
8943                      $parent = $lru[($o['l'] - 1)];
8944                      //Set parent and last pointers

8945                      $this->outlines[$i]['parent'] = $parent;
8946                      $this->outlines[$parent]['last'] = $i;
8947                      if ($o['l'] > $level) {
8948                          //Level increasing: set first pointer

8949                          $this->outlines[$parent]['first'] = $i;
8950                      }
8951                  } else {
8952                      $this->outlines[$i]['parent'] = $nb;
8953                  }
8954                  if (($o['l'] <= $level) AND ($i > 0)) {
8955                      //Set prev and next pointers

8956                      $prev = $lru[$o['l']];
8957                      $this->outlines[$prev]['next'] = $i;
8958                      $this->outlines[$i]['prev'] = $prev;
8959                  }
8960                  $lru[$o['l']] = $i;
8961                  $level = $o['l'];
8962              }
8963              //Outline items

8964              $n = $this->n + 1;
8965              foreach ($this->outlines as $i => $o) {
8966                  $this->_newobj();
8967                  $this->_out('<</Title '.$this->_textstring($o['t']));
8968                  $this->_out('/Parent '.($n + $o['parent']).' 0 R');
8969                  if (isset($o['prev']))
8970                  $this->_out('/Prev '.($n + $o['prev']).' 0 R');
8971                  if (isset($o['next']))
8972                  $this->_out('/Next '.($n + $o['next']).' 0 R');
8973                  if (isset($o['first']))
8974                  $this->_out('/First '.($n + $o['first']).' 0 R');
8975                  if (isset($o['last']))
8976                  $this->_out('/Last '.($n + $o['last']).' 0 R');
8977                  $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))));
8978                  $this->_out('/Count 0>>');
8979                  $this->_out('endobj');
8980              }
8981              //Outline root

8982              $this->_newobj();
8983              $this->OutlineRoot = $this->n;
8984              $this->_out('<</Type /Outlines /First '.$n.' 0 R');
8985              $this->_out('/Last '.($n + $lru[0]).' 0 R>>');
8986              $this->_out('endobj');
8987          }
8988          
8989          
8990          // --- JAVASCRIPT - FORMS ------------------------------

8991          
8992          /*

8993          * Adds a javascript

8994          * @access public

8995          * @author Johannes G�ntert, Nicola Asuni

8996          * @since 2.1.002 (2008-02-12)

8997          */
8998  		public function IncludeJS($script) {
8999              $this->javascript .= $script;
9000          }
9001          
9002          /*

9003          * Create a javascript PDF string.

9004          * @access protected

9005          * @author Johannes G�ntert, Nicola Asuni

9006          * @since 2.1.002 (2008-02-12)

9007          */
9008  		protected function _putjavascript() {
9009              if (empty($this->javascript)) {
9010                  return;
9011              }
9012              // the following two lines are uded to avoid form fields duplication after saving

9013              $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
9014              $js2 = "getField('tcpdfdocsaved').value = 'saved';";
9015              $this->_newobj();
9016              $this->n_js = $this->n;
9017              $this->_out('<<');
9018              $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
9019              $this->_out('>>');
9020              $this->_out('endobj');
9021              $this->_newobj();
9022              $this->_out('<<');
9023              $this->_out('/S /JavaScript');
9024              $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
9025              $this->_out('>>');
9026              $this->_out('endobj');
9027          }
9028          
9029          /*

9030          * Convert color to javascript color.

9031          * @param string $color color name or #RRGGBB

9032          * @access protected

9033          * @author Denis Van Nuffelen, Nicola Asuni

9034          * @since 2.1.002 (2008-02-12)

9035          */
9036  		protected function _JScolor($color) {
9037              static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
9038              if (substr($color,0,1) == '#') {
9039                  return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
9040              }
9041              if (!in_array($color,$aColors)) {
9042                  $this->Error('Invalid color: '.$color);
9043              }
9044              return 'color.'.$color;
9045          }
9046          
9047          /*

9048          * Adds a javascript form field.

9049          * @param string $type field type

9050          * @param string $name field name

9051          * @param int $x horizontal position

9052          * @param int $y vertical position

9053          * @param int $w width

9054          * @param int $h height

9055          * @param array $prop array of properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>

9056          * @access protected

9057          * @author Denis Van Nuffelen, Nicola Asuni

9058          * @since 2.1.002 (2008-02-12)

9059          */
9060  		protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
9061              if ($this->rtl) {
9062                  $x = $x - $w;
9063              }
9064              // the followind avoid fields duplication after saving the document

9065              $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
9066              $k = $this->k;
9067              $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
9068              $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
9069              while (list($key, $val) = each($prop)) {
9070                  if (strcmp(substr($key, -5), 'Color') == 0) {
9071                      $val = $this->_JScolor($val);
9072                  } else {
9073                      $val = "'".$val."'";
9074                  }
9075                  $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
9076              }
9077              if ($this->rtl) {
9078                  $this->x -= $w;
9079              } else {
9080                  $this->x += $w;
9081              }
9082              $this->javascript .= '}';
9083          }
9084          
9085          /*

9086          * Creates a text field

9087          * @param string $name field name

9088          * @param int $w width

9089          * @param int $h height

9090          * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>

9091          * @access public

9092          * @author Denis Van Nuffelen, Nicola Asuni

9093          * @since 2.1.002 (2008-02-12)

9094          */
9095  		public function TextField($name, $w, $h, $prop=array()) {
9096              $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
9097          }
9098          
9099          /*

9100          * Creates a RadioButton field

9101          * @param string $name field name

9102          * @param int $w width

9103          * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>

9104          * @access public

9105          * @author Nicola Asuni

9106          * @since 2.2.003 (2008-03-03)

9107          */
9108  		public function RadioButton($name, $w, $prop=array()) {
9109              if (!isset($prop['strokeColor'])) {
9110                  $prop['strokeColor']='black';
9111              }
9112              $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
9113          }
9114          
9115          /*

9116          * Creates a List-box field

9117          * @param string $name field name

9118          * @param int $w width

9119          * @param int $h height

9120          * @param array $values array containing the list of values.

9121          * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>

9122          * @access public

9123          * @author Nicola Asuni

9124          * @since 2.2.003 (2008-03-03)

9125          */
9126  		public function ListBox($name, $w, $h, $values, $prop=array()) {
9127              if (!isset($prop['strokeColor'])) {
9128                  $prop['strokeColor'] = 'ltGray';
9129              }
9130              $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
9131              $s = '';
9132              foreach ($values as $value) {
9133                  $s .= "'".addslashes($value)."',";
9134              }
9135              $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9136          }
9137          
9138          /*

9139          * Creates a Combo-box field

9140          * @param string $name field name

9141          * @param int $w width

9142          * @param int $h height

9143          * @param array $values array containing the list of values.

9144          * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>

9145          * @access public

9146          * @author Denis Van Nuffelen, Nicola Asuni

9147          * @since 2.1.002 (2008-02-12)

9148          */
9149  		public function ComboBox($name, $w, $h, $values, $prop=array()) {
9150              $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
9151              $s = '';
9152              foreach ($values as $value) {
9153                  $s .= "'".addslashes($value)."',";
9154              }
9155              $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9156          }
9157          
9158          /*

9159          * Creates a CheckBox field

9160          * @param string $name field name

9161          * @param int $w width

9162          * @param boolean $checked define the initial state (default = false).

9163          * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>

9164          * @access public

9165          * @author Denis Van Nuffelen, Nicola Asuni

9166          * @since 2.1.002 (2008-02-12)

9167          */
9168  		public function CheckBox($name, $w, $checked=false, $prop=array()) {
9169              $prop['value'] = ($checked ? 'Yes' : 'Off');
9170              if (!isset($prop['strokeColor'])) {
9171                  $prop['strokeColor'] = 'black';
9172              }
9173              $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
9174          }
9175          
9176          /*

9177          * Creates a button field

9178          * @param string $name field name

9179          * @param int $w width

9180          * @param int $h height

9181          * @param string $caption caption.

9182          * @param string $action action triggered by the button (JavaScript code).

9183          * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>

9184          * @access public

9185          * @author Denis Van Nuffelen, Nicola Asuni

9186          * @since 2.1.002 (2008-02-12)

9187          */
9188  		public function Button($name, $w, $h, $caption, $action, $prop=array()) {
9189              if (!isset($prop['strokeColor'])) {
9190                  $prop['strokeColor'] = 'black';
9191              }
9192              if (!isset($prop['borderStyle'])) {
9193                  $prop['borderStyle'] = 'beveled';
9194              }
9195              $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
9196              $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
9197              $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
9198              $this->javascript .= 'f'.$name.".highlight='push';\n";
9199              $this->javascript .= 'f'.$name.".print=false;\n";
9200          }
9201          
9202          // END JAVASCRIPT - FORMS ------------------------------

9203          
9204          /*

9205          * Enable Write permissions for PDF Reader.

9206          * WARNING: This works only using the Adobe private key with the setSignature() method.

9207          * @access protected

9208          * @author Nicola Asuni

9209          * @since 2.9.000 (2008-03-26)

9210          */
9211  		protected function _putuserrights() {
9212              if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] > 0))) {
9213                  return;
9214              }
9215              $this->_out('/Perms');
9216              $this->_out('<<');
9217              $this->_out('/UR3');
9218              $this->_out('<<');
9219              $this->_out('/Type/Sig');
9220              $this->_out('/Filter/Adobe.PPKLite');
9221              $this->_out('/SubFilter/adbe.pkcs7.detached');
9222              $this->_out('/ByteRange[0 ********** ********** **********]');
9223              $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9224              if ($this->ur) {
9225                  $this->_out('/Reference');
9226                  $this->_out('[');
9227                  $this->_out('<<');
9228                  $this->_out('/Type/SigRef');
9229                  $this->_out('/TransformMethod/UR3');
9230                  $this->_out('/TransformParams');
9231                  $this->_out('<<');
9232                  $this->_out('/Type/TransformParams');
9233                  $this->_out('/V/2.2');
9234                  if (!$this->empty_string($this->ur_document)) {
9235                      $this->_out('/Document['.$this->ur_document.']');
9236                  }
9237                  if (!$this->empty_string($this->ur_annots)) {
9238                      $this->_out('/Annots['.$this->ur_annots.']');
9239                  }
9240                  if (!$this->empty_string($this->ur_form)) {
9241                      $this->_out('/Form['.$this->ur_form.']');
9242                  }
9243                  if (!$this->empty_string($this->ur_signature)) {
9244                      $this->_out('/Signature['.$this->ur_signature.']');
9245                  }            
9246                  $this->_out('>>');
9247                  $this->_out('>>');
9248                  $this->_out(']');
9249              }
9250              $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9251              $this->_out('>>');
9252              $this->_out('>>');
9253          }
9254          
9255          /*

9256          * Add certification signature (DocMDP)

9257          * @access protected

9258          * @author Nicola Asuni

9259          * @since 4.6.008 (2009-05-07)

9260          */
9261  		protected function _putcertification() {
9262              if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] <= 0))) {
9263                  return;
9264              }
9265              $this->_out('/Perms');
9266              $this->_out('<<');
9267              $this->_out('/DocMDP');
9268              $this->_out('<<');
9269              $this->_out('/Type/Sig');
9270              $this->_out('/Filter/Adobe.PPKLite');
9271              $this->_out('/SubFilter/adbe.pkcs7.detached');
9272              $this->_out('/ByteRange[0 ********** ********** **********]');
9273              $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9274              $this->_out('/Reference');
9275              $this->_out('[');
9276              $this->_out('<<');
9277              $this->_out('/Type/SigRef');
9278              $this->_out('/TransformMethod/DocMDP');
9279              $this->_out('/TransformParams');
9280              $this->_out('<<');
9281              $this->_out('/Type/TransformParams');
9282              $this->_out('/V/1.2');
9283              $this->_out('/P '.$this->signature_data['cert_type'].'');
9284              $this->_out('>>');
9285              $this->_out('>>');
9286              $this->_out(']');
9287              $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9288              if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
9289                  $this->_out('/Name '.$this->_textstring($this->signature_data['info']['Name']).'');
9290              }
9291              if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
9292                  $this->_out('/Location '.$this->_textstring($this->signature_data['info']['Location']).'');
9293              }
9294              if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
9295                  $this->_out('/Reason '.$this->_textstring($this->signature_data['info']['Reason']).'');
9296              }
9297              if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
9298                  $this->_out('/ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']).'');
9299              }
9300              $this->_out('>>');
9301              $this->_out('>>');
9302          }
9303          
9304          /*

9305          * Set User's Rights for PDF Reader

9306          * WARNING: This works only using the Adobe private key with the setSignature() method!.

9307          * Check the PDF Reference 8.7.1 Transform Methods, 

9308          * Table 8.105 Entries in the UR transform parameters dictionary

9309          * @param boolean $enable if true enable user's rights on PDF reader

9310          * @param string $document Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data.

9311          * @param string $annots Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations.

9312          * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 

9313          * @param string $signature Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field.

9314          * @access public

9315          * @author Nicola Asuni

9316          * @since 2.9.000 (2008-03-26)

9317          */
9318  		public function setUserRights(
9319                  $enable=true, 
9320                  $document='/FullSave',
9321                  $annots='/Create/Delete/Modify/Copy/Import/Export',
9322                  $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
9323                  $signature='/Modify') {
9324              $this->ur = $enable;
9325              $this->ur_document = $document;
9326              $this->ur_annots = $annots;
9327              $this->ur_form = $form;
9328              $this->ur_signature = $signature;
9329          }
9330          
9331          /*

9332          * Enable document signature (requires the OpenSSL Library).

9333          * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.

9334          * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')

9335          * @param mixed $private_key private key (string or filename prefixed with 'file://')

9336          * @param string $private_key_password password

9337          * @param string $extracerts specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.

9338          * @param int $cert_type The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.

9339          * @parm array $info array of option information: Name, Location, Reason, ContactInfo.

9340          * @access public

9341          * @author Nicola Asuni

9342          * @since 4.6.005 (2009-04-24)

9343          */
9344  		public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
9345              // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.pem -out tcpdf.pem

9346              $this->sign = true;
9347              $this->signature_data = array();
9348              if (strlen($signing_cert) == 0) {
9349                  $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.pem';
9350              }
9351              if (strlen($private_key) == 0) {
9352                  $private_key = $signing_cert;
9353              }
9354              $this->signature_data['signcert'] = $signing_cert;
9355              $this->signature_data['privkey'] = $private_key;
9356              $this->signature_data['password'] = $private_key_password;
9357              $this->signature_data['extracerts'] = $extracerts;
9358              $this->signature_data['cert_type'] = $cert_type;
9359              $this->signature_data['info'] = array();
9360          }
9361          
9362          /*

9363          * Create a new page group.

9364          * NOTE: call this function before calling AddPage()

9365          * @param int $page starting group page (leave empty for next page).

9366          * @access public

9367          * @since 3.0.000 (2008-03-27)

9368          */
9369  		public function startPageGroup($page='') {
9370              if (empty($page)) {
9371                  $page = $this->page + 1;
9372              }
9373              $this->newpagegroup[$page] = true;
9374          }
9375  
9376          /**

9377          * Defines an alias for the total number of pages.

9378          * It will be substituted as the document is closed.

9379          * @param string $alias The alias.

9380          * @access public

9381          * @since 1.4

9382          * @see getAliasNbPages(), PageNo(), Footer()

9383          */
9384  		public function AliasNbPages($alias='{nb}') {
9385              $this->AliasNbPages = $alias;
9386          }
9387          
9388          /**

9389           * Returns the string alias used for the total number of pages.

9390           * If the current font is unicode type, the returned string is surrounded by additional curly braces.

9391           * @return string

9392           * @access public

9393           * @since 4.0.018 (2008-08-08)

9394           * @see AliasNbPages(), PageNo(), Footer()

9395          */
9396  		public function getAliasNbPages() {
9397              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9398                  return '{'.$this->AliasNbPages.'}';
9399              }
9400              return $this->AliasNbPages;
9401          }
9402  
9403          /**

9404          * Defines an alias for the page number.

9405          * It will be substituted as the document is closed.

9406          * @param string $alias The alias.

9407          * @access public

9408          * @since 4.5.000 (2009-01-02)

9409          * @see getAliasNbPages(), PageNo(), Footer()

9410          */
9411  		public function AliasNumPage($alias='{pnb}') {
9412              //Define an alias for total number of pages

9413              $this->AliasNumPage = $alias;
9414          }
9415          
9416          /**

9417           * Returns the string alias used for the page number.

9418           * If the current font is unicode type, the returned string is surrounded by additional curly braces.

9419           * @return string

9420           * @access public

9421           * @since 4.5.000 (2009-01-02)

9422           * @see AliasNbPages(), PageNo(), Footer()

9423          */
9424  		public function getAliasNumPage() {
9425              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9426                  return '{'.$this->AliasNumPage.'}';
9427              }
9428              return $this->AliasNumPage;
9429          }
9430          
9431          /*

9432          * Return the current page in the group.

9433          * @return current page in the group

9434          * @access public

9435          * @since 3.0.000 (2008-03-27)

9436          */
9437  		public function getGroupPageNo() {
9438              return $this->pagegroups[$this->currpagegroup];
9439          }
9440  
9441          /**

9442          * Returns the current group page number formatted as a string.

9443          * @access public

9444          * @since 4.3.003 (2008-11-18)

9445          * @see PaneNo(), formatPageNumber()

9446          */
9447  		public function getGroupPageNoFormatted() {
9448              return $this->formatPageNumber($this->getGroupPageNo());
9449          }
9450          
9451          /*

9452           * Return the alias of the current page group

9453           * If the current font is unicode type, the returned string is surrounded by additional curly braces.

9454           * (will be replaced by the total number of pages in this group).

9455           * @return alias of the current page group

9456           * @access public

9457           * @since 3.0.000 (2008-03-27)

9458          */
9459  		public function getPageGroupAlias() {
9460              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9461                  return '{'.$this->currpagegroup.'}';
9462              }
9463              return $this->currpagegroup;
9464          }
9465          
9466          /*

9467           * Return the alias for the page number on the current page group

9468           * If the current font is unicode type, the returned string is surrounded by additional curly braces.

9469           * (will be replaced by the total number of pages in this group).

9470           * @return alias of the current page group

9471           * @access public

9472           * @since 4.5.000 (2009-01-02)

9473          */
9474  		public function getPageNumGroupAlias() {
9475              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9476                  return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
9477              }
9478              return str_replace('{nb', '{pnb', $this->currpagegroup);
9479          }
9480  
9481          /**

9482          * Format the page numbers.

9483          * This method can be overriden for custom formats.

9484          * @param int $num page number

9485          * @access protected

9486          * @since 4.2.005 (2008-11-06)

9487          */
9488  		protected function formatPageNumber($num) {
9489              return number_format((float)$num, 0, '', '.');
9490          }
9491  
9492          /**

9493          * Format the page numbers on the Table Of Content.

9494          * This method can be overriden for custom formats.

9495          * @param int $num page number

9496          * @access protected

9497          * @since 4.5.001 (2009-01-04)

9498          * @see addTOC()

9499          */
9500  		protected function formatTOCPageNumber($num) {
9501              return number_format((float)$num, 0, '', '.');
9502          }
9503  
9504          /**

9505          * Returns the current page number formatted as a string.

9506          * @access public

9507          * @since 4.2.005 (2008-11-06)

9508          * @see PaneNo(), formatPageNumber()

9509          */
9510  		public function PageNoFormatted() {
9511              return $this->formatPageNumber($this->PageNo());
9512          }
9513  
9514          /*

9515          * Put visibility settings.

9516          * @access protected

9517          * @since 3.0.000 (2008-03-27)

9518          */
9519  		protected function _putocg() {
9520              $this->_newobj();
9521              $this->n_ocg_print = $this->n;
9522              $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
9523              $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
9524              $this->_out('endobj');
9525              $this->_newobj();
9526              $this->n_ocg_view=$this->n;
9527              $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
9528              $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
9529              $this->_out('endobj');
9530          }
9531          
9532          /*

9533          * Set the visibility of the successive elements.

9534          * This can be useful, for instance, to put a background 

9535          * image or color that will show on screen but won't print.

9536          * @param string $v visibility mode. Legal values are: all, print, screen.

9537          * @access public

9538          * @since 3.0.000 (2008-03-27)

9539          */
9540  		public function setVisibility($v) {
9541              if ($this->openMarkedContent) {
9542                  // close existing open marked-content

9543                  $this->_out('EMC');
9544                  $this->openMarkedContent = false;
9545              }
9546              switch($v) {
9547                  case 'print': {
9548                      $this->_out('/OC /OC1 BDC');
9549                      $this->openMarkedContent = true;
9550                      break;
9551                  }
9552                  case 'screen': {
9553                      $this->_out('/OC /OC2 BDC');
9554                      $this->openMarkedContent = true;
9555                      break;
9556                  }
9557                  case 'all': {
9558                      $this->_out('');
9559                      break;
9560                  }
9561                  default: {
9562                      $this->Error('Incorrect visibility: '.$v);
9563                      break;
9564                  }
9565              }
9566              $this->visibility = $v;
9567          }
9568          
9569          /*

9570          * Add transparency parameters to the current extgstate

9571          * @param array $params parameters

9572          * @return the number of extgstates

9573          * @access protected

9574          * @since 3.0.000 (2008-03-27)

9575          */
9576  		protected function addExtGState($parms) {
9577              $n = count($this->extgstates) + 1;
9578              $this->extgstates[$n]['parms'] = $parms;
9579              return $n;
9580          }
9581          
9582          /*

9583          * Add an extgstate

9584          * @param array $gs extgstate

9585          * @access protected

9586          * @since 3.0.000 (2008-03-27)

9587          */
9588  		protected function setExtGState($gs) {
9589              $this->_out(sprintf('/GS%d gs', $gs));
9590          }
9591          
9592          /*

9593          * Put extgstates for object transparency

9594          * @param array $gs extgstate

9595          * @access protected

9596          * @since 3.0.000 (2008-03-27)

9597          */
9598  		protected function _putextgstates() {
9599              $ne = count($this->extgstates);
9600              for ($i = 1; $i <= $ne; ++$i) {
9601                  $this->_newobj();
9602                  $this->extgstates[$i]['n'] = $this->n;
9603                  $this->_out('<</Type /ExtGState');
9604                  foreach ($this->extgstates[$i]['parms'] as $k => $v) {
9605                      $this->_out('/'.$k.' '.$v);
9606                  }
9607                  $this->_out('>>');
9608                  $this->_out('endobj');
9609              }
9610          }
9611          
9612          /*

9613          * Set alpha for stroking (CA) and non-stroking (ca) operations.

9614          * @param float $alpha real value from 0 (transparent) to 1 (opaque)

9615          * @param string $bm blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity

9616          * @access public

9617          * @since 3.0.000 (2008-03-27)

9618          */
9619  		public function setAlpha($alpha, $bm='Normal') {
9620              $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
9621              $this->setExtGState($gs);
9622          }
9623  
9624          /*

9625          * Set the default JPEG compression quality (1-100)

9626          * @param int $quality JPEG quality, integer between 1 and 100

9627          * @access public

9628          * @since 3.0.000 (2008-03-27)

9629          */
9630  		public function setJPEGQuality($quality) {
9631              if (($quality < 1) OR ($quality > 100)) {
9632                  $quality = 75;
9633              }
9634              $this->jpeg_quality = intval($quality);
9635          }
9636          
9637          /*

9638          * Set the default number of columns in a row for HTML tables.

9639          * @param int $cols number of columns

9640          * @access public

9641          * @since 3.0.014 (2008-06-04)

9642          */
9643  		public function setDefaultTableColumns($cols=4) { 
9644              $this->default_table_columns = intval($cols); 
9645          }
9646          
9647          /*

9648          * Set the height of cell repect font height.

9649          * @param int $h cell proportion respect font height (typical value = 1.25).

9650          * @access public

9651          * @since 3.0.014 (2008-06-04)

9652          */
9653  		public function setCellHeightRatio($h) { 
9654              $this->cell_height_ratio = $h; 
9655          }
9656          
9657          /*

9658          * return the height of cell repect font height.

9659          * @access public

9660          * @since 4.0.012 (2008-07-24)

9661          */
9662  		public function getCellHeightRatio() { 
9663              return $this->cell_height_ratio; 
9664          }
9665          
9666          /*

9667          * Set the PDF version (check PDF reference for valid values).

9668          * Default value is 1.t

9669          * @access public

9670          * @since 3.1.000 (2008-06-09)

9671          */
9672  		public function setPDFVersion($version='1.7') { 
9673              $this->PDFVersion = $version;
9674          }
9675          
9676          /*

9677          * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.

9678          * (see Section 8.1 of PDF reference, "Viewer Preferences").

9679          * <ul>

9680          * <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>

9681          * <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>

9682          * <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>

9683          * <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>

9684          * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>

9685          * <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>

9686          * <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>

9687          * <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>

9688          * <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>

9689          * <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>

9690          * <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>

9691          * <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>

9692          * <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>

9693          * <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>

9694          * <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>

9695          * <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>

9696          * </ul>

9697          * @param array $preferences array of options.

9698          * @author Nicola Asuni

9699          * @access public

9700          * @since 3.1.000 (2008-06-09)

9701          */
9702  		public function setViewerPreferences($preferences) { 
9703              $this->viewer_preferences = $preferences;
9704          }
9705          
9706          /**

9707          * Paints a linear colour gradient.

9708          * @param float $x abscissa of the top left corner of the rectangle.

9709          * @param float $y ordinate of the top left corner of the rectangle.

9710          * @param float $w width of the rectangle.

9711          * @param float $h height of the rectangle.

9712          * @param array $col1 first color (RGB components).

9713          * @param array $col2 second color (RGB components).

9714          * @param array $coords array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0).

9715          * @author Andreas W�rmser, Nicola Asuni

9716          * @since 3.1.000 (2008-06-09)

9717          * @access public

9718          */
9719  		public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
9720              $this->Clip($x, $y, $w, $h);
9721              $this->Gradient(2, $col1, $col2, $coords);
9722          }
9723          
9724          /**

9725          * Paints a radial colour gradient.

9726          * @param float $x abscissa of the top left corner of the rectangle.

9727          * @param float $y ordinate of the top left corner of the rectangle.

9728          * @param float $w width of the rectangle.

9729          * @param float $h height of the rectangle.

9730          * @param array $col1 first color (RGB components).

9731          * @param array $col2 second color (RGB components).

9732          * @param array $coords array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined.

9733          * @author Andreas W�rmser, Nicola Asuni

9734          * @since 3.1.000 (2008-06-09)

9735          * @access public

9736          */
9737  		public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
9738              $this->Clip($x, $y, $w, $h);
9739              $this->Gradient(3, $col1, $col2, $coords);
9740          }
9741          
9742          /**

9743          * Paints a coons patch mesh.

9744          * @param float $x abscissa of the top left corner of the rectangle.

9745          * @param float $y ordinate of the top left corner of the rectangle.

9746          * @param float $w width of the rectangle.

9747          * @param float $h height of the rectangle.

9748          * @param array $col1 first color (lower left corner) (RGB components).

9749          * @param array $col2 second color (lower right corner) (RGB components).

9750          * @param array $col3 third color (upper right corner) (RGB components).

9751          * @param array $col4 fourth color (upper left corner) (RGB components).

9752          * @param array $coords <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul>

9753          * @param array $coords_min minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0

9754          * @param array $coords_max maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1

9755          * @author Andreas W�rmser, Nicola Asuni

9756          * @since 3.1.000 (2008-06-09)

9757          * @access public

9758          */
9759  		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) {
9760              $this->Clip($x, $y, $w, $h);        
9761              $n = count($this->gradients) + 1;
9762              $this->gradients[$n]['type'] = 6; //coons patch mesh

9763              //check the coords array if it is the simple array or the multi patch array

9764              if (!isset($coords[0]['f'])) {
9765                  //simple array -> convert to multi patch array

9766                  if (!isset($col1[1])) {
9767                      $col1[1] = $col1[2] = $col1[0];
9768                  }
9769                  if (!isset($col2[1])) {
9770                      $col2[1] = $col2[2] = $col2[0];
9771                  }
9772                  if (!isset($col3[1])) {
9773                      $col3[1] = $col3[2] = $col3[0];
9774                  }
9775                  if (!isset($col4[1])) {
9776                      $col4[1] = $col4[2] = $col4[0];
9777                  }
9778                  $patch_array[0]['f'] = 0;
9779                  $patch_array[0]['points'] = $coords;
9780                  $patch_array[0]['colors'][0]['r'] = $col1[0];
9781                  $patch_array[0]['colors'][0]['g'] = $col1[1];
9782                  $patch_array[0]['colors'][0]['b'] = $col1[2];
9783                  $patch_array[0]['colors'][1]['r'] = $col2[0];
9784                  $patch_array[0]['colors'][1]['g'] = $col2[1];
9785                  $patch_array[0]['colors'][1]['b'] = $col2[2];
9786                  $patch_array[0]['colors'][2]['r'] = $col3[0];
9787                  $patch_array[0]['colors'][2]['g'] = $col3[1];
9788                  $patch_array[0]['colors'][2]['b'] = $col3[2];
9789                  $patch_array[0]['colors'][3]['r'] = $col4[0];
9790                  $patch_array[0]['colors'][3]['g'] = $col4[1];
9791                  $patch_array[0]['colors'][3]['b'] = $col4[2];
9792              } else {
9793                  //multi patch array

9794                  $patch_array = $coords;
9795              }
9796              $bpcd = 65535; //16 BitsPerCoordinate

9797              //build the data stream

9798              $this->gradients[$n]['stream'] = '';
9799              $count_patch = count($patch_array);
9800              for ($i=0; $i < $count_patch; ++$i) {
9801                  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit

9802                  $count_points = count($patch_array[$i]['points']);
9803                  for ($j=0; $j < $count_points; ++$j) {
9804                      //each point as 16 bit

9805                      $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
9806                      if ($patch_array[$i]['points'][$j] < 0) {
9807                          $patch_array[$i]['points'][$j] = 0;
9808                      }
9809                      if ($patch_array[$i]['points'][$j] > $bpcd) {
9810                          $patch_array[$i]['points'][$j] = $bpcd;
9811                      }
9812                      $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
9813                      $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
9814                  }
9815                  $count_cols = count($patch_array[$i]['colors']);
9816                  for ($j=0; $j < $count_cols; ++$j) {
9817                      //each color component as 8 bit

9818                      $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
9819                      $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
9820                      $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
9821                  }
9822              }
9823              //paint the gradient

9824              $this->_out('/Sh'.$n.' sh');
9825              //restore previous Graphic State

9826              $this->_out('Q');
9827          }
9828          
9829          /**

9830          * Set a rectangular clipping area.

9831          * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).

9832          * @param float $y ordinate of the top left corner of the rectangle.

9833          * @param float $w width of the rectangle.

9834          * @param float $h height of the rectangle.

9835          * @author Andreas W�rmser, Nicola Asuni

9836          * @since 3.1.000 (2008-06-09)

9837          * @access protected

9838          */
9839  		protected function Clip($x, $y, $w, $h) {
9840              if ($this->rtl) {
9841                  $x = $this->w - $x - $w;
9842              }
9843              //save current Graphic State

9844              $s = 'q';
9845              //set clipping area

9846              $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
9847              //set up transformation matrix for gradient

9848              $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
9849              $this->_out($s);
9850          }
9851                  
9852          /**

9853          * Output gradient.

9854          * @param int $type type of gradient.

9855          * @param array $col1 first color (RGB components).

9856          * @param array $col2 second color (RGB components).

9857          * @param array $coords array of coordinates.

9858          * @author Andreas W�rmser, Nicola Asuni

9859          * @since 3.1.000 (2008-06-09)

9860          * @access protected

9861          */
9862  		protected function Gradient($type, $col1, $col2, $coords) {
9863              $n = count($this->gradients) + 1;
9864              $this->gradients[$n]['type'] = $type;
9865              if (!isset($col1[1])) {
9866                  $col1[1]=$col1[2]=$col1[0];
9867              }
9868              $this->gradients[$n]['col1'] = sprintf('%.3F %.3F %.3F', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
9869              if (!isset($col2[1])) {
9870                  $col2[1] = $col2[2] = $col2[0];
9871              }
9872              $this->gradients[$n]['col2'] = sprintf('%.3F %.3F %.3F', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
9873              $this->gradients[$n]['coords'] = $coords;
9874              //paint the gradient

9875              $this->_out('/Sh'.$n.' sh');
9876              //restore previous Graphic State

9877              $this->_out('Q');
9878          }
9879          
9880          /**

9881          * Output shaders.

9882          * @author Andreas W�rmser, Nicola Asuni

9883          * @since 3.1.000 (2008-06-09)

9884          * @access protected

9885          */
9886  		function _putshaders() {
9887              foreach ($this->gradients as $id => $grad) {  
9888                  if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
9889                      $this->_newobj();
9890                      $this->_out('<<');
9891                      $this->_out('/FunctionType 2');
9892                      $this->_out('/Domain [0.0 1.0]');
9893                      $this->_out('/C0 ['.$grad['col1'].']');
9894                      $this->_out('/C1 ['.$grad['col2'].']');
9895                      $this->_out('/N 1');
9896                      $this->_out('>>');
9897                      $this->_out('endobj');
9898                      $f1 = $this->n;
9899                  }
9900                  $this->_newobj();
9901                  $this->_out('<<');
9902                  $this->_out('/ShadingType '.$grad['type']);
9903                  $this->_out('/ColorSpace /DeviceRGB');
9904                  if ($grad['type'] == 2) {
9905                      $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
9906                      $this->_out('/Function '.$f1.' 0 R');
9907                      $this->_out('/Extend [true true] ');
9908                      $this->_out('>>');
9909                  } elseif ($grad['type'] == 3) {
9910                      //x0, y0, r0, x1, y1, r1

9911                      //at this this time radius of inner circle is 0

9912                      $this->_out(sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
9913                      $this->_out('/Function '.$f1.' 0 R');
9914                      $this->_out('/Extend [true true] ');
9915                      $this->_out('>>');
9916                  } elseif ($grad['type'] == 6) {
9917                      $this->_out('/BitsPerCoordinate 16');
9918                      $this->_out('/BitsPerComponent 8');
9919                      $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
9920                      $this->_out('/BitsPerFlag 8');
9921                      $this->_out('/Length '.strlen($grad['stream']));
9922                      $this->_out('>>');
9923                      $this->_putstream($grad['stream']);
9924                  }
9925                  $this->_out('endobj');
9926                  $this->gradients[$id]['id'] = $this->n;
9927              }
9928          }
9929  
9930          /**

9931          * Output an arc

9932          * @author Maxime Delorme, Nicola Asuni

9933          * @since 3.1.000 (2008-06-09)

9934          * @access protected

9935          */
9936  		protected function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
9937              $h = $this->h;
9938              $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1*$this->k, ($h-$y1)*$this->k, $x2*$this->k, ($h-$y2)*$this->k, $x3*$this->k, ($h-$y3)*$this->k));
9939          }
9940          
9941          /**

9942          * Draw the sector of a circle.

9943          * It can be used for instance to render pie charts.

9944          * @param float $xc abscissa of the center.

9945          * @param float $yc ordinate of the center.

9946          * @param float $r radius.

9947          * @param float $a start angle (in degrees).

9948          * @param float $b end angle (in degrees).

9949          * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.

9950          * @param float $cw: indicates whether to go clockwise (default: true).

9951          * @param float $o: origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90.

9952          * @author Maxime Delorme, Nicola Asuni

9953          * @since 3.1.000 (2008-06-09)

9954          * @access public

9955          */
9956  		public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
9957              if ($this->rtl) {
9958                  $xc = $this->w - $xc;
9959              }
9960              if ($cw) {
9961                  $d = $b;
9962                  $b = $o - $a;
9963                  $a = $o - $d;
9964              } else {
9965                  $b += $o;
9966                  $a += $o;
9967              }
9968              $a = ($a % 360) + 360;
9969              $b = ($b % 360) + 360;
9970              if ($a > $b) {
9971                  $b +=360;
9972              }
9973              $b = $b / 360 * 2 * M_PI;
9974              $a = $a / 360 * 2 * M_PI;
9975              $d = $b - $a;
9976              if ($d == 0 ) {
9977                  $d = 2 * M_PI;
9978              }
9979              $k = $this->k;
9980              $hp = $this->h;
9981              if ($style=='F') {
9982                  $op = 'f';
9983              } elseif ($style=='FD' or $style=='DF') {
9984                  $op = 'b';
9985              } else {
9986                  $op = 's';
9987              }
9988              if (sin($d/2)) {
9989                  $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
9990              }
9991              //first put the center

9992              $this->_out(sprintf('%.2F %.2F m', ($xc)*$k, ($hp-$yc)*$k));
9993              //put the first point

9994              $this->_out(sprintf('%.2F %.2F l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
9995              //draw the arc

9996              if ($d < (M_PI/2)) {
9997                  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
9998              } else {
9999                  $b = $a + $d/4;
10000                  $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
10001                  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10002                  $a = $b;
10003                  $b = $a + $d/4;
10004                  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10005                  $a = $b;
10006                  $b = $a + $d/4;
10007                  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b) );
10008                  $a = $b;
10009                  $b = $a + $d/4;
10010                  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10011              }
10012              //terminate drawing

10013              $this->_out($op);
10014          }
10015          
10016          /**

10017          * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.

10018          * Only vector drawing is supported, not text or bitmap. 

10019          * 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).

10020          * @param string $file Name of the file containing the image.

10021          * @param float $x Abscissa of the upper-left corner.

10022          * @param float $y Ordinate of the upper-left corner.

10023          * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.

10024          * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.

10025          * @param mixed $link URL or identifier returned by AddLink().

10026          * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.

10027          * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>

10028          * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>

10029          * @param mixed $border Indicates if borders must be drawn around the image. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>

10030          * @author Valentin Schmidt, Nicola Asuni

10031          * @since 3.1.000 (2008-06-09)

10032          * @access public

10033          */
10034  		public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0) {
10035              if ($x === '') {
10036                  $x = $this->x;
10037              }
10038              if ($y === '') {
10039                  $y = $this->y;
10040              }
10041              $k = $this->k;
10042              $data = file_get_contents($file);
10043              if ($data === false) {
10044                  $this->Error('EPS file not found: '.$file);
10045              }
10046              $regs = array();
10047              // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)

10048              preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator

10049              if (count($regs) > 1) {
10050                  $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"

10051                  if (strpos($version_str, 'Adobe Illustrator') !== false) {
10052                      $versexp = explode(' ', $version_str);
10053                      $version = (float)array_pop($versexp);
10054                      if ($version >= 9) {
10055                          $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
10056                      }
10057                  }
10058              }
10059              // strip binary bytes in front of PS-header

10060              $start = strpos($data, '%!PS-Adobe');
10061              if ($start > 0) {
10062                  $data = substr($data, $start);
10063              }
10064              // find BoundingBox params

10065              preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
10066              if (count($regs) > 1) {
10067                  list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
10068              } else {
10069                  $this->Error('No BoundingBox found in EPS file: '.$file);
10070              }
10071              $start = strpos($data, '%%EndSetup');
10072              if ($start === false) {
10073                  $start = strpos($data, '%%EndProlog');
10074              }
10075              if ($start === false) {
10076                  $start = strpos($data, '%%BoundingBox');
10077              }
10078              $data = substr($data, $start);
10079              $end = strpos($data, '%%PageTrailer');
10080              if ($end===false) {
10081                  $end = strpos($data, 'showpage');
10082              }
10083              if ($end) {
10084                  $data = substr($data, 0, $end);
10085              }
10086              if ($w > 0) {
10087                  $scale_x = $w / (($x2 - $x1) / $k);
10088                  if ($h > 0) {
10089                      $scale_y = $h / (($y2 - $y1) / $k);
10090                  } else {
10091                      $scale_y = $scale_x;
10092                      $h = ($y2 - $y1) / $k * $scale_y;
10093                  }
10094              } else {
10095                  if ($h > 0) {
10096                      $scale_y = $h / (($y2 - $y1) / $k);
10097                      $scale_x = $scale_y;
10098                      $w = ($x2-$x1) / $k * $scale_x;
10099                  } else {
10100                      $w = ($x2 - $x1) / $k;
10101                      $h = ($y2 - $y1) / $k;
10102                  }
10103              }
10104              // Check whether we need a new page first as this does not fit

10105              if ($this->checkPageBreak($h, $y)) {
10106                  $y = $this->GetY() + $this->cMargin;
10107              }
10108              // set bottomcoordinates

10109              $this->img_rb_y = $y + $h;
10110              // set alignment

10111              if ($this->rtl) {
10112                  if ($palign == 'L') {
10113                      $ximg = $this->lMargin;
10114                      // set right side coordinate

10115                      $this->img_rb_x = $ximg + $w;
10116                  } elseif ($palign == 'C') {
10117                      $ximg = ($this->w - $x - $w) / 2;
10118                      // set right side coordinate

10119                      $this->img_rb_x = $ximg + $w;
10120                  } else {
10121                      $ximg = $this->w - $x - $w;
10122                      // set left side coordinate

10123                      $this->img_rb_x = $ximg;
10124                  }
10125              } else {
10126                  if ($palign == 'R') {
10127                      $ximg = $this->w - $this->rMargin - $w;
10128                      // set left side coordinate

10129                      $this->img_rb_x = $ximg;
10130                  } elseif ($palign == 'C') {
10131                      $ximg = ($this->w - $x - $w) / 2;
10132                      // set right side coordinate

10133                      $this->img_rb_x = $ximg + $w;
10134                  } else {
10135                      $ximg = $x;
10136                      // set right side coordinate

10137                      $this->img_rb_x = $ximg + $w;
10138                  }
10139              }
10140              if ($useBoundingBox) {
10141                  $dx = $ximg * $k - $x1;
10142                  $dy = $y * $k - $y1;
10143              } else {
10144                  $dx = $ximg * $k;
10145                  $dy = $y * $k;
10146              }
10147              // save the current graphic state

10148              $this->_out('q'.$this->epsmarker);
10149              // translate

10150              $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
10151              // scale

10152              if (isset($scale_x)) {
10153                  $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
10154              }
10155              // handle pc/unix/mac line endings

10156              preg_match('/[\r\n]+/s', $data, $regs);
10157              $lines = explode($regs[0], $data);
10158              $u=0;
10159              $cnt = count($lines);
10160              for ($i=0; $i < $cnt; ++$i) {
10161                  $line = $lines[$i];
10162                  if (($line == '') OR ($line{0} == '%')) {
10163                      continue;
10164                  }
10165                  $len = strlen($line);
10166                  $chunks = explode(' ', $line);
10167                  $cmd = array_pop($chunks);
10168                  // RGB

10169                  if (($cmd == 'Xa') OR ($cmd == 'XA')) {
10170                      $b = array_pop($chunks); 
10171                      $g = array_pop($chunks); 
10172                      $r = array_pop($chunks);
10173                      $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!

10174                      continue;
10175                  }
10176                  switch ($cmd) {
10177                      case 'm':
10178                      case 'l':
10179                      case 'v':
10180                      case 'y':
10181                      case 'c':
10182                      case 'k':
10183                      case 'K':
10184                      case 'g':
10185                      case 'G':
10186                      case 's':
10187                      case 'S':
10188                      case 'J':
10189                      case 'j':
10190                      case 'w':
10191                      case 'M':
10192                      case 'd':
10193                      case 'n':
10194                      case 'v': {
10195                          $this->_out($line);
10196                          break;
10197                      }
10198                      case 'x': {// custom fill color
10199                          list($c,$m,$y,$k) = $chunks;
10200                          $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k');
10201                          break;
10202                      }
10203                      case 'X': { // custom stroke color
10204                          list($c,$m,$y,$k) = $chunks;
10205                          $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K');
10206                          break;
10207                      }
10208                      case 'Y':
10209                      case 'N':
10210                      case 'V':
10211                      case 'L':
10212                      case 'C': {
10213                          $line{$len-1} = strtolower($cmd);
10214                          $this->_out($line);
10215                          break;
10216                      }
10217                      case 'b':
10218                      case 'B': {
10219                          $this->_out($cmd . '*');
10220                          break;
10221                      }
10222                      case 'f':
10223                      case 'F': {
10224                          if ($u > 0) {
10225                              $isU = false;
10226                              $max = min($i+5, $cnt);
10227                              for ($j=$i+1; $j < $max; ++$j)
10228                                $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
10229                              if ($isU) {
10230                                  $this->_out('f*');
10231                              }
10232                          } else {
10233                              $this->_out('f*');
10234                          }
10235                          break;
10236                      }
10237                      case '*u': {
10238                          ++$u;
10239                          break;
10240                      }
10241                      case '*U': {
10242                          --$u;
10243                          break;
10244                      }
10245                  }
10246              }
10247              // restore previous graphic state

10248              $this->_out($this->epsmarker.'Q');
10249              if (!empty($border)) {
10250                  $bx = $x;
10251                  $by = $y;
10252                  $this->x = $x;
10253                  $this->y = $y;
10254                  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
10255                  $this->x = $bx;
10256                  $this->y = $by;
10257              }
10258              if ($link) {
10259                  $this->Link($ximg, $y, $w, $h, $link, 0);
10260              }
10261              // set pointer to align the successive text/objects

10262              switch($align) {
10263                  case 'T':{
10264                      $this->y = $y;
10265                      $this->x = $this->img_rb_x;
10266                      break;
10267                  }
10268                  case 'M':{
10269                      $this->y = $y + round($h/2);
10270                      $this->x = $this->img_rb_x;
10271                      break;
10272                  }
10273                  case 'B':{
10274                      $this->y = $this->img_rb_y;
10275                      $this->x = $this->img_rb_x;
10276                      break;
10277                  }
10278                  case 'N':{
10279                      $this->SetY($this->img_rb_y);
10280                      break;
10281                  }
10282                  default:{
10283                      break;
10284                  }
10285              }
10286              $this->endlinex = $this->img_rb_x;
10287          }
10288          
10289          /**

10290            * Set document barcode.

10291           * @param string $bc barcode

10292           * @access public

10293           */
10294  		public function setBarcode($bc='') {
10295              $this->barcode = $bc;
10296          }
10297          
10298          /**

10299            * Get current barcode.

10300           * @return string

10301           * @access public

10302           * @since 4.0.012 (2008-07-24)

10303           */
10304  		public function getBarcode() {
10305              return $this->barcode;
10306          }
10307          
10308          /**

10309            * Print a Linear Barcode.

10310            * @param string $code code to print

10311            * @param string $type type of barcode.

10312           * @param int $x x position in user units

10313           * @param int $y y position in user units

10314           * @param int $w width in user units

10315           * @param int $h height in user units

10316           * @param float $xres width of the smallest bar in user units

10317           * @param array $style array of options:<ul><li>string $style['position'] barcode position inside the specified width: L = left (default for LTR); C = center; R = right (default for RTL); S = stretch</li><li>boolean $style['border'] if true prints a border around the barcode</li><li>int $style['padding'] padding to leave around the barcode in user units</li><li>array $style['fgcolor'] color array for bars and text</li><li>mixed $style['bgcolor'] color array for background or false for transparent</li><li>boolean $style["text"] boolean if true prints text below the barcode</li><li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li><li>int $style['stretchtext']: 0 = disabled; 1 = horizontal scaling only if necessary; 2 = forced horizontal scaling; 3 = character spacing only if necessary; 4 = forced character spacing</li></ul>

10318           * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>

10319           * @author Nicola Asuni

10320           * @since 3.1.000 (2008-06-09)

10321           * @access public

10322           */
10323  		public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
10324              if ($this->empty_string($code)) {
10325                  return;
10326              }
10327              require_once(dirname(__FILE__).'/barcodes.php');
10328              // save current graphic settings

10329              $gvars = $this->getGraphicVars();
10330              // create new barcode object

10331              $barcodeobj = new TCPDFBarcode($code, $type);
10332              $arrcode = $barcodeobj->getBarcodeArray();
10333              if ($arrcode === false) {
10334                  $this->Error('Error in 1D barcode string');
10335              }
10336              // set default values

10337              if (!isset($style['position'])) {
10338                  if ($this->rtl) {
10339                      $style['position'] = 'R';
10340                  } else {
10341                      $style['position'] = 'L';
10342                  }
10343              }
10344              if (!isset($style['padding'])) {
10345                  $style['padding'] = 0;
10346              }
10347              if (!isset($style['fgcolor'])) {
10348                  $style['fgcolor'] = array(0,0,0); // default black

10349              }
10350              if (!isset($style['bgcolor'])) {
10351                  $style['bgcolor'] = false; // default transparent

10352              }
10353              if (!isset($style['border'])) {
10354                  $style['border'] = false;
10355              }
10356              if (!isset($style['text'])) {
10357                  $style['text'] = false;
10358                  $fontsize = 0;
10359              }
10360              if ($style['text'] AND isset($style['font'])) {
10361                  if (isset($style['fontsize'])) {
10362                      $fontsize = $style['fontsize'];
10363                  } else {
10364                      $fontsize = 0;
10365                  }
10366                  $this->SetFont($style['font'], '', $fontsize);
10367              }
10368              if (!isset($style['stretchtext'])) {
10369                  $style['stretchtext'] = 4;
10370              }
10371              // set foreground color

10372              $this->SetDrawColorArray($style['fgcolor']);
10373              $this->SetTextColorArray($style['fgcolor']);
10374              if ($this->empty_string($w) OR ($w <= 0)) {
10375                  if ($this->rtl) {
10376                      $w = $this->x - $this->lMargin;
10377                  } else {
10378                      $w = $this->w - $this->rMargin - $this->x;
10379                  }
10380              }
10381              if ($this->empty_string($x)) {
10382                  $x = $this->GetX();
10383              }
10384              if ($this->rtl) {
10385                  $x = $this->w - $x;
10386              }
10387              if ($this->empty_string($y)) {
10388                  $y = $this->GetY();
10389              }
10390              if ($this->empty_string($xres)) {
10391                  $xres = 0.4;
10392              }
10393              $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']);
10394              $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']);
10395              if ($this->empty_string($h) OR ($h <= 0)) {
10396                  $h = 10 + $extraspace;
10397              }
10398              if ($this->checkPageBreak($h)) {
10399                  $y = $this->y;
10400              }
10401              // maximum bar heigth

10402              $barh = $h - $extraspace;
10403              switch ($style['position']) {
10404                  case 'L': { // left
10405                      if ($this->rtl) {
10406                          $xpos = $x - $w;
10407                      } else {
10408                          $xpos = $x;
10409                      }
10410                      break;
10411                  }
10412                  case 'C': { // center
10413                      $xdiff = (($w - $fbw) / 2);
10414                      if ($this->rtl) {
10415                          $xpos = $x - $w + $xdiff;
10416                      } else {
10417                          $xpos = $x + $xdiff;
10418                      }
10419                      break;
10420                  }
10421                  case 'R': { // right
10422                      if ($this->rtl) {
10423                          $xpos = $x - $fbw;
10424                      } else {
10425                          $xpos = $x + $w - $fbw;
10426                      }
10427                      break;
10428                  }
10429                  case 'S': { // stretch
10430                      $fbw = $w;
10431                      $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw'];
10432                      if ($this->rtl) {
10433                          $xpos = $x - $w;
10434                      } else {
10435                          $xpos = $x;
10436                      }
10437                      break;
10438                  }
10439              }
10440              $xpos_rect = $xpos;
10441              $xpos = $xpos_rect + $style['padding'];
10442              $xpos_text = $xpos;
10443              // barcode is always printed in LTR direction

10444              $tempRTL = $this->rtl;
10445              $this->rtl = false;
10446              // print background color

10447              if ($style['bgcolor']) {
10448                  $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style['bgcolor']);
10449              } elseif ($style['border']) {
10450                  $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
10451              }
10452              // print bars

10453              if ($arrcode !== false) {
10454                  foreach ($arrcode['bcode'] as $k => $v) {
10455                      $bw = ($v['w'] * $xres);
10456                      if ($v['t']) {
10457                          // draw a vertical bar

10458                          $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']);
10459                          $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh  / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
10460                      }
10461                      $xpos += $bw;
10462                  }
10463              }
10464              // print text

10465              if ($style['text']) {
10466                  // print text

10467                  $this->x = $xpos_text;
10468                  $this->y = $y + $style['padding'] + $barh; 
10469                  $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']);
10470              }
10471              // restore original direction

10472              $this->rtl = $tempRTL;
10473              // restore previous settings

10474              $this->setGraphicVars($gvars);
10475              // set bottomcoordinates

10476              $this->img_rb_y = $y + $h;
10477              if ($this->rtl) {
10478                  // set left side coordinate

10479                  $this->img_rb_x = ($this->w - $x - $w);
10480              } else {
10481                  // set right side coordinate

10482                  $this->img_rb_x = $x + $w;
10483              }
10484              // set pointer to align the successive text/objects

10485              switch($align) {
10486                  case 'T':{
10487                      $this->y = $y;
10488                      $this->x = $this->img_rb_x;
10489                      break;
10490                  }
10491                  case 'M':{
10492                      $this->y = $y + round($h/2);
10493                      $this->x = $this->img_rb_x;
10494                      break;
10495                  }
10496                  case 'B':{
10497                      $this->y = $this->img_rb_y;
10498                      $this->x = $this->img_rb_x;
10499                      break;
10500                  }
10501                  case 'N':{
10502                      $this->SetY($this->img_rb_y);
10503                      break;
10504                  }
10505                  default:{
10506                      break;
10507                  }
10508              }
10509          }
10510          
10511          /**

10512            * This function is DEPRECATED, please use the new write1DBarcode() function.

10513           * @param int $x x position in user units

10514           * @param int $y y position in user units

10515           * @param int $w width in user units

10516           * @param int $h height position in user units

10517           * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)

10518           * @param string $style barcode style

10519           * @param string $font font for text

10520           * @param int $xres x resolution

10521           * @param string $code code to print

10522           * @deprecated deprecated since version 3.1.000 (2008-06-10)

10523           * @access public

10524           * @see write1DBarcode()

10525           */
10526  		public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
10527              // convert old settings for the new write1DBarcode() function.

10528              $xres = 1 / $xres;
10529              $newstyle = array(
10530                  'position' => 'L',
10531                  'border' => false,
10532                  'padding' => 0,
10533                  'fgcolor' => array(0,0,0),
10534                  'bgcolor' => false,
10535                  'text' => true,
10536                  'font' => $font,
10537                  'fontsize' => 8,
10538                  'stretchtext' => 4
10539              );
10540              if ($style & 1) {
10541                  $newstyle['border'] = true;
10542              }
10543              if ($style & 2) {
10544                  $newstyle['bgcolor'] = false;
10545              }
10546              if ($style & 4) {
10547                  $newstyle['position'] = 'C';
10548              } elseif ($style & 8) {
10549                  $newstyle['position'] = 'L';
10550              } elseif ($style & 16) {
10551                  $newstyle['position'] = 'R';
10552              }
10553              if ($style & 128) {
10554                  $newstyle['text'] = true;
10555              }
10556              if ($style & 256) {
10557                  $newstyle['stretchtext'] = 4;
10558              }
10559              $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
10560          }
10561          
10562          /**

10563            * Print 2D Barcode.

10564            * @param string $code code to print

10565            * @param string $type type of barcode.

10566           * @param int $x x position in user units

10567           * @param int $y y position in user units

10568           * @param int $w width in user units

10569           * @param int $h height in user units

10570           * @param array $style array of options:<ul><li>boolean $style['border'] if true prints a border around the barcode</li><li>int $style['padding'] padding to leave around the barcode in user units</li><li>array $style['fgcolor'] color array for bars and text</li><li>mixed $style['bgcolor'] color array for background or false for transparent</li></ul>

10571           * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>

10572           * @author Nicola Asuni

10573           * @since 4.5.037 (2009-04-07)

10574           * @access public

10575           */
10576  		public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') {
10577              if ($this->empty_string($code)) {
10578                  return;
10579              }
10580              require_once(dirname(__FILE__).'/2dbarcodes.php');
10581              // save current graphic settings

10582              $gvars = $this->getGraphicVars();
10583              // create new barcode object

10584              $barcodeobj = new TCPDF2DBarcode($code, $type);
10585              $arrcode = $barcodeobj->getBarcodeArray();
10586              if ($arrcode === false) {
10587                  $this->Error('Error in 2D barcode string');
10588              }
10589              // set default values

10590              if (!isset($style['padding'])) {
10591                  $style['padding'] = 0;
10592              }
10593              if (!isset($style['fgcolor'])) {
10594                  $style['fgcolor'] = array(0,0,0); // default black

10595              }
10596              if (!isset($style['bgcolor'])) {
10597                  $style['bgcolor'] = false; // default transparent

10598              }
10599              if (!isset($style['border'])) {
10600                  $style['border'] = false;
10601              }
10602              // set foreground color

10603              $this->SetDrawColorArray($style['fgcolor']);
10604              if ($this->empty_string($x)) {
10605                  $x = $this->GetX();
10606              }
10607              if ($this->rtl) {
10608                  $x = $this->w - $x;
10609              }
10610              if ($this->empty_string($y)) {
10611                  $y = $this->GetY();
10612              }
10613              if ($this->empty_string($w) OR ($w <= 0)) {
10614                  if ($this->rtl) {
10615                      $w = $x - $this->lMargin;
10616                  } else {
10617                      $w = $this->w - $this->rMargin - $x;
10618                  }
10619              }
10620              if ($this->empty_string($h) OR ($h <= 0)) {
10621                  // 2d barcodes are square by default

10622                  $h = $w;
10623              }
10624              if ($this->checkPageBreak($h)) {
10625                  $y = $this->y;
10626              }
10627              // calculate barcode size (excluding padding)

10628              $bw = $w - (2 * $style['padding']);
10629              $bh = $h - (2 * $style['padding']);
10630              // calculate starting coordinates

10631              if ($this->rtl) {
10632                  $xpos = $x - $w;
10633              } else {
10634                  $xpos = $x;
10635              }
10636              $xpos += $style['padding'];
10637              $ypos = $y + $style['padding'];
10638              // barcode is always printed in LTR direction

10639              $tempRTL = $this->rtl;
10640              $this->rtl = false;
10641              // print background color

10642              if ($style['bgcolor']) {
10643                  $this->Rect($x, $y, $w, $h, 'DF', '', $style['bgcolor']);
10644              } elseif ($style['border']) {
10645                  $this->Rect($x, $y, $w, $h, 'D');
10646              }
10647              // print barcode cells

10648              if ($arrcode !== false) {
10649                  $rows = $arrcode['num_rows'];
10650                  $cols = $arrcode['num_cols'];
10651                  // calculate dimension of single barcode cell

10652                  $cw = $bw / $cols;
10653                  $ch = $bh / $rows;
10654                  // for each row

10655                  for ($r = 0; $r < $rows; ++$r) {
10656                      $xr = $xpos;
10657                      // for each column

10658                      for ($c = 0; $c < $cols; ++$c) {
10659                          if ($arrcode['bcode'][$r][$c] == 1) {
10660                              // draw a single barcode cell

10661                              $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']);
10662                          }
10663                          $xr += $cw;
10664                      }
10665                      $ypos += $ch;
10666                  }
10667              }
10668              // restore original direction

10669              $this->rtl = $tempRTL;
10670              // restore previous settings

10671              $this->setGraphicVars($gvars);
10672              // set bottomcoordinates

10673              $this->img_rb_y = $y + $h;
10674              if ($this->rtl) {
10675                  // set left side coordinate

10676                  $this->img_rb_x = ($this->w - $x - $w);
10677              } else {
10678                  // set right side coordinate

10679                  $this->img_rb_x = $x + $w;
10680              }
10681              // set pointer to align the successive text/objects

10682              switch($align) {
10683                  case 'T':{
10684                      $this->y = $y;
10685                      $this->x = $this->img_rb_x;
10686                      break;
10687                  }
10688                  case 'M':{
10689                      $this->y = $y + round($h/2);
10690                      $this->x = $this->img_rb_x;
10691                      break;
10692                  }
10693                  case 'B':{
10694                      $this->y = $this->img_rb_y;
10695                      $this->x = $this->img_rb_x;
10696                      break;
10697                  }
10698                  case 'N':{
10699                      $this->SetY($this->img_rb_y);
10700                      break;
10701                  }
10702                  default:{
10703                      break;
10704                  }
10705              }
10706          }
10707          
10708          /**

10709           * Returns an array containing current margins:

10710           * <ul>

10711                  <li>$ret['left'] = left  margin</li>

10712                  <li>$ret['right'] = right margin</li>

10713                  <li>$ret['top'] = top margin</li>

10714                  <li>$ret['bottom'] = bottom margin</li>

10715                  <li>$ret['header'] = header margin</li>

10716                  <li>$ret['footer'] = footer margin</li>

10717                  <li>$ret['cell'] = cell margin</li>

10718           * </ul>

10719           * @return array containing all margins measures 

10720           * @access public

10721           * @since 3.2.000 (2008-06-23)

10722           */
10723  		public function getMargins() {
10724              $ret = array(
10725                  'left' => $this->lMargin,
10726                  'right' => $this->rMargin,
10727                  'top' => $this->tMargin,
10728                  'bottom' => $this->bMargin,
10729                  'header' => $this->header_margin,
10730                  'footer' => $this->footer_margin,
10731                  'cell' => $this->cMargin,
10732              );
10733              return $ret;
10734          }
10735          
10736          /**

10737           * Returns an array containing original margins:

10738           * <ul>

10739                  <li>$ret['left'] = left  margin</li>

10740                  <li>$ret['right'] = right margin</li>

10741           * </ul>

10742           * @return array containing all margins measures 

10743           * @access public

10744           * @since 4.0.012 (2008-07-24)

10745           */
10746  		public function getOriginalMargins() {
10747              $ret = array(
10748                  'left' => $this->original_lMargin,
10749                  'right' => $this->original_rMargin
10750              );
10751              return $ret;
10752          }
10753          
10754          /**

10755           * Returns the current font size.

10756           * @return current font size

10757           * @access public

10758           * @since 3.2.000 (2008-06-23)

10759           */
10760  		public function getFontSize() {
10761              return $this->FontSize;
10762          }
10763          
10764          /**

10765           * Returns the current font size in points unit.

10766           * @return current font size in points unit

10767           * @access public

10768           * @since 3.2.000 (2008-06-23)

10769           */
10770  		public function getFontSizePt() {
10771              return $this->FontSizePt;
10772          }
10773  
10774          /**

10775           * Returns the current font family name.

10776           * @return string current font family name

10777           * @access public

10778           * @since 4.3.008 (2008-12-05)

10779           */
10780  		public function getFontFamily() {
10781              return $this->FontFamily;
10782          }
10783  
10784          /**

10785           * Returns the current font style.

10786           * @return string current font style

10787           * @access public

10788           * @since 4.3.008 (2008-12-05)

10789           */
10790  		public function getFontStyle() {
10791              return $this->FontStyle;
10792          }
10793          
10794          /**

10795           * Prints a cell (rectangular area) with optional borders, background color and html text string. 

10796           * 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 />

10797           * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.

10798           * @param float $w Cell width. If 0, the cell extends up to the right margin.

10799           * @param float $h Cell minimum height. The cell extends automatically if needed.

10800           * @param float $x upper-left corner X coordinate

10801           * @param float $y upper-left corner Y coordinate

10802           * @param string $html html text to print. Default value: empty string.

10803           * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>

10804           * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>

10805      Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.

10806           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.

10807           * @param boolean $reseth if true reset the last cell height (default true).

10808           * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>

10809           * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.

10810           * @access public

10811           * @uses MultiCell()

10812           * @see Multicell(), writeHTML()

10813           */
10814  		public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) {
10815              return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
10816          }
10817          
10818          /**

10819            * Returns the HTML DOM array.

10820            * <ul><li>$dom[$key]['tag'] = true if tag, false otherwise;</li><li>$dom[$key]['value'] = tag name or text;</li><li>$dom[$key]['opening'] = true if opening tag, false otherwise;</li><li>$dom[$key]['attribute'] = array of attributes (attribute name is the key);</li><li>$dom[$key]['style'] = array of style attributes (attribute name is the key);</li><li>$dom[$key]['parent'] = id of parent element;</li><li>$dom[$key]['fontname'] = font family name;</li><li>$dom[$key]['fontstyle'] = font style;</li><li>$dom[$key]['fontsize'] = font size in points;</li><li>$dom[$key]['bgcolor'] = RGB array of background color;</li><li>$dom[$key]['fgcolor'] = RGB array of foreground color;</li><li>$dom[$key]['width'] = width in pixels;</li><li>$dom[$key]['height'] = height in pixels;</li><li>$dom[$key]['align'] = text alignment;</li><li>$dom[$key]['cols'] = number of colums in table;</li><li>$dom[$key]['rows'] = number of rows in table;</li></ul>

10821           * @param string $html html code

10822           * @return array

10823           * @access protected

10824           * @since 3.2.000 (2008-06-20)

10825           */
10826  		protected function getHtmlDomArray($html) {
10827              // remove all unsupported tags (the line below lists all supported tags)

10828              $html = strip_tags($html, '<marker/><a><b><blockquote><br><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>');
10829              //replace some blank characters

10830              $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag

10831              $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
10832              $html = preg_replace('@(\r\n|\r)@', "\n", $html);
10833              $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
10834              $html = strtr($html, $repTable);
10835              while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html)) {
10836                  // preserve newlines on <pre> tag

10837                  $html = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html);
10838              }
10839              $html = str_replace("\n", ' ', $html);
10840              // remove extra spaces from code

10841              $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '</\\1>', $html);
10842              $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html);
10843              $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
10844              $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
10845              $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
10846              $html = preg_replace('/<img/', ' <img', $html);
10847              $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span></span>', $html);
10848              $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag

10849              // trim string

10850              $html = preg_replace('/^[\s]+/', '', $html);
10851              $html = preg_replace('/[\s]+$/', '', $html);
10852              // pattern for generic tag

10853              $tagpattern = '/(<[^>]+>)/';
10854              // explodes the string

10855              $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
10856              // count elements

10857              $maxel = count($a);
10858              $elkey = 0;
10859              $key = 0;
10860              // create an array of elements

10861              $dom = array();
10862              $dom[$key] = array();
10863              // set first void element

10864              $dom[$key]['tag'] = false;
10865              $dom[$key]['value'] = '';
10866              $dom[$key]['parent'] = 0;
10867              $dom[$key]['fontname'] = $this->FontFamily;
10868              $dom[$key]['fontstyle'] = $this->FontStyle;
10869              $dom[$key]['fontsize'] = $this->FontSizePt;
10870              $dom[$key]['bgcolor'] = false;
10871              $dom[$key]['fgcolor'] = $this->fgcolor;
10872              $dom[$key]['align'] = '';
10873              $dom[$key]['listtype'] = '';
10874              $thead = false; // true when we are inside the THEAD tag

10875              ++$key;
10876              $level = array();
10877              array_push($level, 0); // root

10878              while ($elkey < $maxel) {
10879                  $dom[$key] = array();
10880                  $element = $a[$elkey];
10881                  $dom[$key]['elkey'] = $elkey;
10882                  if (preg_match($tagpattern, $element)) {
10883                      // html tag

10884                      $element = substr($element, 1, -1);
10885                      // get tag name

10886                      preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
10887                      $tagname = strtolower($tag[1]);
10888                      // check if we are inside a table header

10889                      if ($tagname == 'thead') {
10890                          if ($element{0} == '/') {
10891                              $thead = false;
10892                          } else {
10893                              $thead = true;
10894                          }
10895                          ++$elkey;
10896                          continue;
10897                      }
10898                      $dom[$key]['tag'] = true;
10899                      $dom[$key]['value'] = $tagname;
10900                      if ($element{0} == '/') {
10901                          // closing html tag

10902                          $dom[$key]['opening'] = false;
10903                          $dom[$key]['parent'] = end($level);
10904                          array_pop($level);
10905                          $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
10906                          $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
10907                          $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
10908                          $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
10909                          $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
10910                          $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
10911                          if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
10912                              $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
10913                          }
10914                          // set the number of columns in table tag

10915                          if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
10916                              $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
10917                          }
10918                          if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
10919                              $dom[($dom[$key]['parent'])]['content'] = '';
10920                              for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
10921                                  $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
10922                              }
10923                              $key = $i;
10924                          }
10925                          // store header rows on a new table

10926                          if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] == true)) {
10927                              if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
10928                                  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
10929                              }
10930                              for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
10931                                  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
10932                              }
10933                          }
10934                          if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
10935                              $dom[($dom[$key]['parent'])]['thead'] .= '</table>';
10936                          }
10937                      } else {
10938                          // opening html tag

10939                          $dom[$key]['opening'] = true;
10940                          $dom[$key]['parent'] = end($level);
10941                          if (substr($element, -1, 1) != '/') {
10942                              // not self-closing tag

10943                              array_push($level, $key);
10944                              $dom[$key]['self'] = false;
10945                          } else {
10946                              $dom[$key]['self'] = true;
10947                          }
10948                          // copy some values from parent

10949                          $parentkey = 0;
10950                          if ($key > 0) {
10951                              $parentkey = $dom[$key]['parent'];
10952                              $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
10953                              $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
10954                              $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
10955                              $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
10956                              $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
10957                              $dom[$key]['align'] = $dom[$parentkey]['align'];
10958                              $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
10959                          }
10960                          // get attributes

10961                          preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER);
10962                          $dom[$key]['attribute'] = array(); // reset attribute array

10963                          while (list($id, $name) = each($attr_array[1])) {
10964                              $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
10965                          }
10966                          // split style attributes

10967                          if (isset($dom[$key]['attribute']['style'])) {
10968                              // get style attributes

10969                              preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
10970                              $dom[$key]['style'] = array(); // reset style attribute array

10971                              while (list($id, $name) = each($style_array[1])) {
10972                                  $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
10973                              }
10974                              // --- get some style attributes ---

10975                              if (isset($dom[$key]['style']['font-family'])) {
10976                                  // font family

10977                                  if (isset($dom[$key]['style']['font-family'])) {
10978                                      $fontslist = split(',', strtolower($dom[$key]['style']['font-family']));
10979                                      foreach ($fontslist as $font) {
10980                                          $font = trim(strtolower($font));
10981                                          if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
10982                                              $dom[$key]['fontname'] = $font;
10983                                              break;
10984                                          }
10985                                      }
10986                                  }
10987                              }
10988                              // list-style-type

10989                              if (isset($dom[$key]['style']['list-style-type'])) {
10990                                  $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
10991                                  if ($dom[$key]['listtype'] == 'inherit') {
10992                                      $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
10993                                  }
10994                              }
10995                              // font size

10996                              if (isset($dom[$key]['style']['font-size'])) {
10997                                  $fsize = trim($dom[$key]['style']['font-size']);
10998                                  switch ($fsize) {
10999                                      // absolute-size

11000                                      case 'xx-small': {
11001                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
11002                                          break;
11003                                      }
11004                                      case 'x-small': {
11005                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
11006                                          break;
11007                                      }
11008                                      case 'small': {
11009                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
11010                                          break;
11011                                      }
11012                                      case 'medium': {
11013                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'];
11014                                          break;
11015                                      }
11016                                      case 'large': {
11017                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
11018                                          break;
11019                                      }
11020                                      case 'x-large': {
11021                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
11022                                          break;
11023                                      }
11024                                      case 'xx-large': {
11025                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
11026                                          break;
11027                                      }
11028                                      // relative-size

11029                                      case 'smaller': {
11030                                          $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
11031                                          break;
11032                                      }
11033                                      case 'larger': {
11034                                          $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
11035                                          break;
11036                                      }
11037                                      default: {
11038                                          $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
11039                                      }
11040                                  }
11041                              }
11042                              // font style

11043                              if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
11044                                  $dom[$key]['fontstyle'] .= 'B';
11045                              }
11046                              if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
11047                                  $dom[$key]['fontstyle'] .= '"I';
11048                              }
11049                              // font color

11050                              if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
11051                                  $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
11052                              }
11053                              // background color

11054                              if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
11055                                  $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
11056                              }
11057                              // text-decoration

11058                              if (isset($dom[$key]['style']['text-decoration'])) {
11059                                  $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
11060                                  foreach ($decors as $dec) {
11061                                      $dec = trim($dec);
11062                                      if (!$this->empty_string($dec)) {
11063                                          if ($dec{0} == 'u') {
11064                                              $dom[$key]['fontstyle'] .= 'U';
11065                                          } elseif ($dec{0} == 'l') {
11066                                              $dom[$key]['fontstyle'] .= 'D';
11067                                          }
11068                                      }
11069                                  }
11070                              }
11071                              // check for width attribute

11072                              if (isset($dom[$key]['style']['width'])) {
11073                                  $dom[$key]['width'] = $dom[$key]['style']['width'];
11074                              }
11075                              // check for height attribute

11076                              if (isset($dom[$key]['style']['height'])) {
11077                                  $dom[$key]['height'] = $dom[$key]['style']['height'];
11078                              }
11079                              // check for text alignment

11080                              if (isset($dom[$key]['style']['text-align'])) {
11081                                  $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
11082                              }
11083                              // check for border attribute

11084                              if (isset($dom[$key]['style']['border'])) {
11085                                  $dom[$key]['attribute']['border'] = $dom[$key]['style']['border'];
11086                              }
11087                          }
11088                          // check for font tag

11089                          if ($dom[$key]['value'] == 'font') {
11090                              // font family

11091                              if (isset($dom[$key]['attribute']['face'])) {
11092                                  $fontslist = split(',', strtolower($dom[$key]['attribute']['face']));
11093                                  foreach ($fontslist as $font) {
11094                                      $font = trim(strtolower($font));
11095                                      if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11096                                          $dom[$key]['fontname'] = $font;
11097                                          break;
11098                                      }
11099                                  }
11100                              }
11101                              // font size

11102                              if (isset($dom[$key]['attribute']['size'])) {
11103                                  if ($key > 0) {
11104                                      if ($dom[$key]['attribute']['size']{0} == '+') {
11105                                          $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
11106                                      } elseif ($dom[$key]['attribute']['size']{0} == '-') {
11107                                          $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
11108                                      } else {
11109                                          $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11110                                      }
11111                                  } else {
11112                                      $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11113                                  }
11114                              }
11115                          }
11116                          // force natural alignment for lists

11117                          if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
11118                              AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
11119                              if ($this->rtl) {
11120                                  $dom[$key]['align'] = 'R';
11121                              } else {
11122                                  $dom[$key]['align'] = 'L';
11123                              }
11124                          }
11125                          if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
11126                              $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
11127                          }
11128                          if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
11129                              $dom[$key]['fontstyle'] .= 'B';
11130                          }
11131                          if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
11132                              $dom[$key]['fontstyle'] .= 'I';
11133                          }
11134                          if ($dom[$key]['value'] == 'u') {
11135                              $dom[$key]['fontstyle'] .= 'U';
11136                          }
11137                          if ($dom[$key]['value'] == 'del') {
11138                              $dom[$key]['fontstyle'] .= 'D';
11139                          }
11140                          if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
11141                              $dom[$key]['fontname'] = $this->default_monospaced_font;
11142                          }
11143                          if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
11144                              $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
11145                              $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
11146                              $dom[$key]['fontstyle'] .= 'B';
11147                          }
11148                          if (($dom[$key]['value'] == 'table')) {
11149                              $dom[$key]['rows'] = 0; // number of rows

11150                              $dom[$key]['trids'] = array(); // IDs of TR elements

11151                              $dom[$key]['thead'] = ''; // table header rows

11152                          }
11153                          if (($dom[$key]['value'] == 'tr')) {
11154                              $dom[$key]['cols'] = 0;
11155                              // store the number of rows on table element

11156                              ++$dom[($dom[$key]['parent'])]['rows'];
11157                              // store the TR elements IDs on table element

11158                              array_push($dom[($dom[$key]['parent'])]['trids'], $key);
11159                              if ($thead) {
11160                                  $dom[$key]['thead'] = true;
11161                              } else {
11162                                  $dom[$key]['thead'] = false;
11163                              }
11164                          }
11165                          if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
11166                              if (isset($dom[$key]['attribute']['colspan'])) {
11167                                  $colspan = intval($dom[$key]['attribute']['colspan']);
11168                              } else {
11169                                  $colspan = 1;
11170                              }
11171                              $dom[$key]['attribute']['colspan'] = $colspan;
11172                              $dom[($dom[$key]['parent'])]['cols'] += $colspan;
11173                          }
11174                          // set foreground color attribute

11175                          if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
11176                              $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
11177                          }
11178                          // set background color attribute

11179                          if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
11180                              $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
11181                          }
11182                          // check for width attribute

11183                          if (isset($dom[$key]['attribute']['width'])) {
11184                              $dom[$key]['width'] = $dom[$key]['attribute']['width'];
11185                          }
11186                          // check for height attribute

11187                          if (isset($dom[$key]['attribute']['height'])) {
11188                              $dom[$key]['height'] = $dom[$key]['attribute']['height'];
11189                          }
11190                          // check for text alignment

11191                          if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
11192                              $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
11193                          }
11194                      } // end opening tag

11195                  } else {
11196                      // text

11197                      $dom[$key]['tag'] = false;
11198                      $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
11199                      $dom[$key]['parent'] = end($level);
11200                  }
11201                  ++$elkey;
11202                  ++$key;
11203              }
11204              return $dom;
11205          }
11206          
11207          /**

11208           * Allows to preserve some HTML formatting (limited support).<br />

11209           * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.

11210           * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, small, span, strong, sub, sup, table, td, th, tr, u, ul, 

11211           * @param string $html text to display

11212           * @param boolean $ln if true add a new line after text (default = true)

11213           * @param int $fill Indicates if the background must be painted (true) or transparent (false).

11214           * @param boolean $reseth if true reset the last cell height (default false).

11215           * @param boolean $cell if true add the default cMargin space to each Write (default false).

11216           * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>

11217           * @access public

11218           */
11219  		public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
11220              $gvars = $this->getGraphicVars();
11221              // store current values

11222              $prevPage = $this->page;
11223              $prevlMargin = $this->lMargin;
11224              $prevrMargin = $this->rMargin;
11225              $curfontname = $this->FontFamily;
11226              $curfontstyle = $this->FontStyle;
11227              $curfontsize = $this->FontSizePt;    
11228              $this->newline = true;
11229              $minstartliney = $this->y;
11230              $yshift = 0;
11231              $startlinepage = $this->page;
11232              $newline = true;
11233              $loop = 0;
11234              $curpos = 0;
11235              $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul','tcpdf');
11236              $this->premode = false;
11237              if (isset($this->PageAnnots[$this->page])) {
11238                  $pask = count($this->PageAnnots[$this->page]);
11239              } else {
11240                  $pask = 0;
11241              }
11242              if (isset($this->footerlen[$this->page])) {
11243                  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11244              } else {
11245                  $this->footerpos[$this->page] = $this->pagelen[$this->page];
11246              }
11247              $startlinepos = $this->footerpos[$this->page];
11248              $lalign = $align;
11249              $plalign = $align;
11250              if ($this->rtl) {
11251                  $w = $this->x - $this->lMargin;
11252              } else {
11253                  $w = $this->w - $this->rMargin - $this->x;
11254              }
11255              $w -= (2 * $this->cMargin);
11256              if ($cell) {
11257                  if ($this->rtl) {
11258                      $this->x -= $this->cMargin;
11259                  } else {
11260                      $this->x += $this->cMargin;
11261                  }
11262              }
11263              if ($this->customlistindent >= 0) {
11264                  $this->listindent = $this->customlistindent;
11265              } else {
11266                  $this->listindent = $this->GetStringWidth('0000');
11267              }
11268              $this->listnum = 0;
11269              if (($this->empty_string($this->lasth)) OR ($reseth)) {
11270                  //set row height

11271                  $this->lasth = $this->FontSize * $this->cell_height_ratio; 
11272              }
11273              $dom = $this->getHtmlDomArray($html);
11274              $maxel = count($dom);
11275              $key = 0;
11276              while ($key < $maxel) {
11277                  if ($dom[$key]['tag'] OR ($key == 0)) {
11278                      if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
11279                          $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
11280                      }
11281                      // vertically align image in line

11282                      if ((!$this->newline)
11283                          AND ($dom[$key]['value'] == 'img')
11284                          AND (isset($dom[$key]['attribute']['height']))
11285                          AND ($dom[$key]['attribute']['height'] > 0)) {
11286                          // get image height

11287                          $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px');
11288                          if (!$this->InFooter) {
11289                              // check for page break

11290                              $this->checkPageBreak($imgh);
11291                          }
11292                          if ($this->page > $startlinepage) {
11293                              // fix lines splitted over two pages

11294                              if (isset($this->footerlen[$startlinepage])) {
11295                                  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11296                              }
11297                              // line to be moved one page forward

11298                              $pagebuff = $this->getPageBuffer($startlinepage);
11299                              $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11300                              $tstart = substr($pagebuff, 0, $startlinepos);
11301                              $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11302                              // remove line start from previous page

11303                              $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11304                              $pagebuff = $this->getPageBuffer($this->page);
11305                              $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11306                              $tend = substr($pagebuff, $this->intmrk[$this->page]);
11307                              // add line start to current page

11308                              $yshift = $minstartliney - $this->y;
11309                              $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11310                              $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11311                              // shift the annotations and links

11312                              if (isset($this->PageAnnots[$startlinepage])) {
11313                                  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11314                                      if ($pak >= $pask) {
11315                                          $this->PageAnnots[$this->page][] = $pac;
11316                                          unset($this->PageAnnots[$startlinepage][$pak]);
11317                                          $npak = count($this->PageAnnots[$this->page]) - 1;
11318                                          $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11319                                      }
11320                                  }
11321                              }
11322                              $startlinepos = $this->intmrk[$this->page];
11323                              $startlinepage = $this->page;
11324                              $startliney = $this->y;
11325                          }
11326                          
11327                          $this->y += (($curfontsize / $this->k) - $imgh);
11328                          $minstartliney = min($this->y, $minstartliney);
11329                          
11330                      } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
11331                          // account for different font size

11332                          $pfontname = $curfontname;
11333                          $pfontstyle = $curfontstyle;
11334                          $pfontsize = $curfontsize;
11335                          $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
11336                          $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
11337                          $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
11338                          if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
11339                              $this->SetFont($fontname, $fontstyle, $fontsize);
11340                              $this->lasth = $this->FontSize * $this->cell_height_ratio;
11341                              if (is_numeric($fontsize) AND ($fontsize > 0)
11342                                  AND is_numeric($curfontsize) AND ($curfontsize > 0)
11343                                  AND ($fontsize != $curfontsize) AND (!$this->newline)
11344                                  AND ($key < ($maxel - 1))
11345                                  ) {
11346                                  if ((!$this->newline) AND ($this->page > $startlinepage)) {
11347                                      // fix lines splitted over two pages

11348                                      if (isset($this->footerlen[$startlinepage])) {
11349                                          $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11350                                      }
11351                                      // line to be moved one page forward

11352                                      $pagebuff = $this->getPageBuffer($startlinepage);
11353                                      $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11354                                      $tstart = substr($pagebuff, 0, $startlinepos);
11355                                      $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11356                                      // remove line start from previous page

11357                                      $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11358                                      $pagebuff = $this->getPageBuffer($this->page);
11359                                      $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11360                                      $tend = substr($pagebuff, $this->intmrk[$this->page]);
11361                                      // add line start to current page

11362                                      $yshift = $minstartliney - $this->y;
11363                                      $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11364                                      $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11365                                      // shift the annotations and links

11366                                      if (isset($this->PageAnnots[$startlinepage])) {
11367                                          foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11368                                              if ($pak >= $pask) {
11369                                                  $this->PageAnnots[$this->page][] = $pac;
11370                                                  unset($this->PageAnnots[$startlinepage][$pak]);
11371                                                  $npak = count($this->PageAnnots[$this->page]) - 1;
11372                                                  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11373                                              }
11374                                          }
11375                                      }
11376                                  }
11377                                  $this->y += (($curfontsize - $fontsize) / $this->k);
11378                                  $minstartliney = min($this->y, $minstartliney);
11379                              }
11380                              $curfontname = $fontname;
11381                              $curfontstyle = $fontstyle;
11382                              $curfontsize = $fontsize;
11383                          }
11384                      }
11385                      if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) {
11386                          $plalign = '';
11387                      }
11388                      // get current position on page buffer

11389                      $curpos = $this->pagelen[$startlinepage];
11390                      if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
11391                          $this->SetFillColorArray($dom[$key]['bgcolor']);
11392                          $wfill = true;
11393                      } else {
11394                          $wfill = $fill | false;
11395                      }
11396                      if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
11397                          $this->SetTextColorArray($dom[$key]['fgcolor']);
11398                      }
11399                      if (isset($dom[$key]['align'])) {
11400                          $lalign = $dom[$key]['align'];
11401                      }
11402                      if ($this->empty_string($lalign)) {
11403                          $lalign = $align;
11404                      }
11405                  }
11406                  // align lines

11407                  if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
11408                      $newline = true;
11409                      // we are at the beginning of a new line

11410                      if (isset($startlinex)) {
11411                          $yshift = $minstartliney - $startliney;
11412                          if (($yshift > 0) OR ($this->page > $startlinepage)) {
11413                              $yshift = 0;
11414                          }
11415                          if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
11416                              // the last line must be shifted to be aligned as requested

11417                              $linew = abs($this->endlinex - $startlinex);
11418                              $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
11419                              if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11420                                  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11421                                  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
11422                              } elseif (isset($opentagpos)) {
11423                                  $midpos = $opentagpos;
11424                              } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11425                                  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11426                                  $midpos = $this->footerpos[$startlinepage];
11427                              } else {
11428                                  $midpos = 0;
11429                              }
11430                              if ($midpos > 0) {
11431                                  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
11432                                  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
11433                              } else {
11434                                  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
11435                                  $pend = '';
11436                              }
11437                              // calculate shifting amount

11438                              $tw = $w;
11439                              if ($this->lMargin != $prevlMargin) {
11440                                  $tw += ($prevlMargin - $this->lMargin);
11441                              }
11442                              if ($this->rMargin != $prevrMargin) {
11443                                  $tw += ($prevrMargin - $this->rMargin);
11444                              }
11445                              $mdiff = abs($tw - $linew);
11446                              $t_x = 0;
11447                              if ($plalign == 'C') {
11448                                  if ($this->rtl) {
11449                                      $t_x = -($mdiff / 2);
11450                                  } else {
11451                                      $t_x = ($mdiff / 2);
11452                                  }
11453                              } elseif (($plalign == 'R') AND (!$this->rtl)) {
11454                                  // right alignment on LTR document

11455                                  $t_x = $mdiff;    
11456                              } elseif (($plalign == 'L') AND ($this->rtl)) {
11457                                  // left alignment on RTL document

11458                                  $t_x = -$mdiff;
11459                              } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
11460                                  // Justification

11461                                  if ($this->rtl OR $this->tmprtl) {
11462                                      $t_x = $this->lMargin - $this->endlinex;
11463                                  }
11464                                  $no = 0;
11465                                  $ns = 0;
11466                                  $pmidtemp = $pmid;
11467                                  // escape special characters

11468                                  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11469                                  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11470                                  // search spaces

11471                                  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
11472                                      $maxkk = count($lnstring[1]) - 1;
11473                                      for ($kk=0; $kk <= $maxkk; ++$kk) {
11474                                          // restore special characters

11475                                          $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
11476                                          $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
11477                                          if ($kk == $maxkk) {
11478                                              if ($this->rtl OR $this->tmprtl) {
11479                                                  $tvalue = ltrim($lnstring[1][$kk]);
11480                                              } else {
11481                                                  $tvalue = rtrim($lnstring[1][$kk]);
11482                                              }
11483                                          } else {
11484                                              $tvalue = $lnstring[1][$kk];
11485                                          }
11486                                          // count spaces on line

11487                                          $no += substr_count($lnstring[1][$kk], chr(32));
11488                                          $ns += substr_count($tvalue, chr(32));
11489                                      }
11490                                      if ($this->rtl OR $this->tmprtl) {
11491                                          $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32)));
11492                                      }
11493                                      // calculate additional space to add to each space

11494                                      $spacewidth = (($tw - $linew + (($no - $ns) * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1)) * $this->k;
11495                                      $spacewidthu = ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize / $this->k;
11496                                      $nsmax = $ns;
11497                                      $ns = 0;
11498                                      reset($lnstring);
11499                                      $offset = 0;
11500                                      $strcount = 0;
11501                                      $prev_epsposbeg = 0;
11502                                      global $spacew;
11503                                      while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
11504                                          if ($this->rtl OR $this->tmprtl) {
11505                                              $spacew = ($spacewidth * ($nsmax - $ns));
11506                                          } else {
11507                                              $spacew = ($spacewidth * $ns);
11508                                          }
11509                                          $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
11510                                          $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
11511                                          $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
11512                                          if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
11513                                              OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
11514                                              // shift EPS images

11515                                              $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
11516                                              $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
11517                                              $pmid_b = substr($pmid, 0, $epsposbeg);
11518                                              $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
11519                                              $pmid_e = substr($pmid, $epsposend);
11520                                              $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
11521                                              $offset = $epsposend;
11522                                              continue;
11523                                          }
11524                                          $prev_epsposbeg = $epsposbeg;
11525                                          $currentxpos = 0;
11526                                          // shift blocks of code

11527                                          switch ($strpiece[2][0]) {
11528                                              case 'Td':
11529                                              case 'cm':
11530                                              case 'm':
11531                                              case 'l': {
11532                                                  // get current X position

11533                                                  preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11534                                                  $currentxpos = $xmatches[1];
11535                                                  if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
11536                                                      if ($strcount == $maxkk) {
11537                                                          if ($this->rtl OR $this->tmprtl) {
11538                                                              $tvalue = $lnstring[1][$strcount];
11539                                                          } else {
11540                                                              $tvalue = rtrim($lnstring[1][$strcount]);
11541                                                          }
11542                                                      } else {
11543                                                          $tvalue = $lnstring[1][$strcount];
11544                                                      }
11545                                                      $ns += substr_count($tvalue, chr(32));
11546                                                      ++$strcount;
11547                                                  }
11548                                                  if ($this->rtl OR $this->tmprtl) {
11549                                                      $spacew = ($spacewidth * ($nsmax - $ns));
11550                                                  }
11551                                                  // justify block

11552                                                  $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11553                                                      create_function('$matches', 'global $spacew;
11554                                                      $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11555                                                      return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
11556                                                  break;
11557                                              }
11558                                              case 're': {
11559                                                  // get current X position

11560                                                  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11561                                                  $currentxpos = $xmatches[1];
11562                                                  // justify block

11563                                                  $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11564                                                      create_function('$matches', 'global $spacew;
11565                                                      $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11566                                                      return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
11567                                                  break;
11568                                              }
11569                                              case 'c': {
11570                                                  // get current X position

11571                                                  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11572                                                  $currentxpos = $xmatches[1];
11573                                                  // justify block

11574                                                  $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11575                                                      create_function('$matches', 'global $spacew;
11576                                                      $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
11577                                                      $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
11578                                                      $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
11579                                                      return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
11580                                                  break;
11581                                              }
11582                                          }
11583                                          // shift the annotations and links

11584                                          if (isset($this->PageAnnots[$this->page])) {
11585                                              foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11586                                                  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
11587                                                      $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
11588                                                      $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
11589                                                      break;
11590                                                  }
11591                                              }
11592                                          }
11593                                      } // end of while

11594                                      // remove markers

11595                                      $pmid = str_replace('x*#!#*x', '', $pmid);
11596                                      if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11597                                          // multibyte characters

11598                                          $spacew = $spacewidthu;
11599                                          $pmidtemp = $pmid;
11600                                          // escape special characters

11601                                          $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11602                                          $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11603                                          $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
11604                                                      create_function('$matches', 'global $spacew;
11605                                                      $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
11606                                                      $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
11607                                                      return "[(".str_replace(chr(0).chr(32), ") ".(-2830 * $spacew)." (", $matches[1]).")]";'), $pmidtemp);
11608                                          $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
11609                                          $endlinepos = strlen($pstart."\n".$pmid."\n");
11610                                      } else {
11611                                          // non-unicode (single-byte characters)

11612                                          $rs = sprintf("%.3F Tw", $spacewidth);
11613                                          $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
11614                                          $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
11615                                          $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
11616                                      }
11617                                  }
11618                              } // end of J

11619                              if (($t_x != 0) OR ($yshift < 0)) {
11620                                  // shift the line

11621                                  $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
11622                                  $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
11623                                  $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
11624                                  // shift the annotations and links

11625                                  if (isset($this->PageAnnots[$this->page])) {
11626                                      foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11627                                          if ($pak >= $pask) {
11628                                              $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
11629                                              $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
11630                                          }
11631                                      }
11632                                  }
11633                                  $this->y -= $yshift;
11634                              }
11635                          }
11636                      }
11637                      $this->newline = false;
11638                      $pbrk = $this->checkPageBreak($this->lasth);
11639                      $this->SetFont($fontname, $fontstyle, $fontsize);
11640                      if ($wfill) {
11641                          $this->SetFillColorArray($this->bgcolor);
11642                      }
11643                      $startlinex = $this->x;
11644                      $startliney = $this->y;
11645                      $minstartliney = $this->y;
11646                      $startlinepage = $this->page;
11647                      if (isset($endlinepos) AND (!$pbrk)) {
11648                          $startlinepos = $endlinepos;
11649                          unset($endlinepos);
11650                      } else {
11651                          if (isset($this->footerlen[$this->page])) {
11652                              $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11653                          } else {
11654                              $this->footerpos[$this->page] = $this->pagelen[$this->page];
11655                          }
11656                          $startlinepos = $this->footerpos[$this->page];
11657                      }
11658                      $plalign = $lalign;
11659                      if (isset($this->PageAnnots[$this->page])) {
11660                          $pask = count($this->PageAnnots[$this->page]);
11661                      } else {
11662                          $pask = 0;
11663                      }
11664                  }
11665                  if (isset($opentagpos)) {
11666                      unset($opentagpos);
11667                  }
11668                  if ($dom[$key]['tag']) {
11669                      if ($dom[$key]['opening']) {
11670                          if ($dom[$key]['value'] == 'table') {
11671                              if ($this->rtl) {
11672                                  $wtmp = $this->x - $this->lMargin;
11673                              } else {
11674                                  $wtmp = $this->w - $this->rMargin - $this->x;
11675                              }
11676                              $wtmp -= (2 * $this->cMargin);
11677                              // calculate cell width

11678                              if (isset($dom[$key]['width'])) {
11679                                  $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11680                              } else {
11681                                  $table_width = $wtmp;
11682                              }
11683                          }
11684                          // table content is handled in a special way

11685                          if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11686                              $trid = $dom[$key]['parent'];
11687                              $table_el = $dom[$trid]['parent'];
11688                              if (!isset($dom[$table_el]['cols'])) {
11689                                  $dom[$table_el]['cols'] = $trid['cols'];
11690                              }
11691                              $oldmargin = $this->cMargin;
11692                              if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
11693                                  $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
11694                              } else {
11695                                  $currentcmargin = 0;        
11696                              }
11697                              $this->cMargin = $currentcmargin;
11698                              if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
11699                                  $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px');
11700                              } else {
11701                                  $cellspacing = 0;
11702                              }
11703                              if ($this->rtl) {
11704                                  $cellspacingx = -$cellspacing;
11705                              } else {
11706                                  $cellspacingx = $cellspacing;
11707                              }
11708                              $colspan = $dom[$key]['attribute']['colspan'];
11709                              $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols']));
11710                              if (isset($dom[$key]['width'])) {
11711                                  $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11712                              } else {
11713                                  $cellw = $wtmp;
11714                              }
11715                              if (isset($dom[$key]['height'])) {
11716                                  // minimum cell height

11717                                  $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
11718                              } else {
11719                                  $cellh = 0;
11720                              }
11721                              $cellw -= $cellspacing;
11722                              if (isset($dom[$key]['content'])) {
11723                                  $cell_content = $dom[$key]['content'];
11724                              } else {
11725                                  $cell_content = '&nbsp;';
11726                              }
11727                              $tagtype = $dom[$key]['value'];
11728                              $parentid = $key;
11729                              while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
11730                                  // move $key index forward

11731                                  ++$key;
11732                              }
11733                              if (!isset($dom[$trid]['startpage'])) {
11734                                  $dom[$trid]['startpage'] = $this->page;
11735                              } else {
11736                                  $this->setPage($dom[$trid]['startpage']);
11737                              }
11738                              if (!isset($dom[$trid]['starty'])) {
11739                                  $dom[$trid]['starty'] = $this->y;
11740                              } else {
11741                                  $this->y = $dom[$trid]['starty'];
11742                              }
11743                              if (!isset($dom[$trid]['startx'])) {
11744                                  $dom[$trid]['startx'] = $this->x;
11745                              }
11746                              $this->x += ($cellspacingx / 2);                        
11747                              if (isset($dom[$parentid]['attribute']['rowspan'])) {
11748                                  $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
11749                              } else {
11750                                  $rowspan = 1;
11751                              }
11752                              // skip row-spanned cells started on the previous rows

11753                              if (isset($dom[$table_el]['rowspans'])) {
11754                                  $rsk = 0;
11755                                  $rskmax = count($dom[$table_el]['rowspans']);
11756                                  while ($rsk < $rskmax) {
11757                                      $trwsp = $dom[$table_el]['rowspans'][$rsk];
11758                                      $rsstartx = $trwsp['startx'];
11759                                      $rsendx = $trwsp['endx'];
11760                                      // account for margin changes

11761                                      if ($trwsp['startpage'] < $this->page) {
11762                                          if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
11763                                              $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
11764                                              $rsstartx -= $dl;
11765                                              $rsendx -= $dl;
11766                                          } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
11767                                              $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
11768                                              $rsstartx += $dl;
11769                                              $rsendx += $dl;
11770                                          }
11771                                      }
11772                                      if  (($trwsp['rowspan'] > 0)
11773                                          AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps))
11774                                          AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps))
11775                                          AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) {
11776                                          // set the starting X position of the current cell

11777                                          $this->x = $rsendx + $cellspacingx;
11778                                          if (($trwsp['rowspan'] == 1)
11779                                              AND (isset($dom[$trid]['endy']))
11780                                              AND (isset($dom[$trid]['endpage']))
11781                                              AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) {
11782                                              // set ending Y position for row

11783                                              $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11784                                              $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
11785                                          }
11786                                          $rsk = 0;
11787                                      } else {
11788                                          ++$rsk;
11789                                      }
11790                                  }
11791                              }
11792                              // add rowspan information to table element

11793                              if ($rowspan > 1) {
11794                                  if (isset($this->footerlen[$this->page])) {
11795                                      $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11796                                  } else {
11797                                      $this->footerpos[$this->page] = $this->pagelen[$this->page];
11798                                  }
11799                                  $trintmrkpos = $this->footerpos[$this->page];
11800                                  $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
11801                              }
11802                              $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
11803                              if ($rowspan > 1) {
11804                                  $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
11805                              }
11806                              // push background colors

11807                              if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
11808                                  $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
11809                              }
11810                              $prevLastH = $this->lasth;
11811                              // ****** write the cell content ******

11812                              $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
11813                              $this->lasth = $prevLastH;
11814                              $this->cMargin = $oldmargin;
11815                              $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
11816                              // update the end of row position

11817                              if ($rowspan <= 1) {
11818                                  if (isset($dom[$trid]['endy'])) {
11819                                      if ($this->page == $dom[$trid]['endpage']) {
11820                                          $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
11821                                      } elseif ($this->page > $dom[$trid]['endpage']) {
11822                                          $dom[$trid]['endy'] = $this->y;
11823                                      }
11824                                  } else {
11825                                      $dom[$trid]['endy'] = $this->y;
11826                                  }
11827                                  if (isset($dom[$trid]['endpage'])) {
11828                                      $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
11829                                  } else {
11830                                      $dom[$trid]['endpage'] = $this->page;
11831                                  }                                
11832                              } else {
11833                                  // account for row-spanned cells

11834                                  $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
11835                                  $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
11836                                  $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
11837                              }
11838                              if (isset($dom[$table_el]['rowspans'])) {
11839                                  // update endy and endpage on rowspanned cells

11840                                  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
11841                                      if ($trwsp['rowspan'] > 0) {
11842                                          if (isset($dom[$trid]['endpage'])) {
11843                                              if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
11844                                                  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11845                                              } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) {
11846                                                  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
11847                                                  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
11848                                              } else {
11849                                                  $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
11850                                              }
11851                                          }
11852                                      }
11853                                  }
11854                              }
11855                              $this->x += ($cellspacingx / 2);                            
11856                          } else {
11857                              // opening tag (or self-closing tag)

11858                              if (!isset($opentagpos)) {
11859                                  if (!$this->InFooter) {
11860                                      if (isset($this->footerlen[$this->page])) {
11861                                          $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11862                                      } else {
11863                                          $this->footerpos[$this->page] = $this->pagelen[$this->page];
11864                                      }
11865                                      $opentagpos = $this->footerpos[$this->page];
11866                                  }
11867                              }
11868                              $this->openHTMLTagHandler($dom, $key, $cell);
11869                          }
11870                      } else {
11871                          // closing tag

11872                          $this->closeHTMLTagHandler($dom, $key, $cell);
11873                      }
11874                  } elseif (strlen($dom[$key]['value']) > 0) {
11875                      // print list-item

11876                      if (!$this->empty_string($this->lispacer)) {
11877                          $this->SetFont($pfontname, $pfontstyle, $pfontsize);
11878                          $this->lasth = $this->FontSize * $this->cell_height_ratio;
11879                          $minstartliney = $this->y;
11880                          $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
11881                          $this->SetFont($curfontname, $curfontstyle, $curfontsize);
11882                          $this->lasth = $this->FontSize * $this->cell_height_ratio;
11883                          if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
11884                              $this->y += (($pfontsize - $curfontsize) / $this->k);
11885                              $minstartliney = min($this->y, $minstartliney);
11886                          }
11887                      }
11888                      // text

11889                      $this->htmlvspace = 0;
11890                      if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) {
11891                          // reverse spaces order

11892                          $len1 = strlen($dom[$key]['value']);
11893                          $lsp = $len1 - strlen(ltrim($dom[$key]['value']));
11894                          $rsp = $len1 - strlen(rtrim($dom[$key]['value']));
11895                          $tmpstr = '';
11896                          if ($rsp > 0) {
11897                              $tmpstr .= substr($dom[$key]['value'], -$rsp);
11898                          }
11899                          $tmpstr .= trim($dom[$key]['value']);
11900                          if ($lsp > 0) {
11901                              $tmpstr .= substr($dom[$key]['value'], 0, $lsp);
11902                          }
11903                          $dom[$key]['value'] = $tmpstr;
11904                      }
11905                      if ($newline) {
11906                          if (!$this->premode) {
11907                              if (($this->rtl OR $this->tmprtl)) {
11908                                  $dom[$key]['value'] = rtrim($dom[$key]['value']);
11909                              } else {
11910                                  $dom[$key]['value'] = ltrim($dom[$key]['value']);
11911                              }
11912                          }
11913                          $newline = false;
11914                          $firstblock = true;
11915                      } else {
11916                          $firstblock = false;
11917                      }
11918                      $strrest = '';
11919                      if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
11920                          // HTML <a> Link

11921                          $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']);
11922                      } else {
11923                          $ctmpmargin = $this->cMargin;
11924                          $this->cMargin = 0;
11925                          // ****** write only until the end of the line and get the rest ******

11926                          $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock);
11927                          $this->cMargin = $ctmpmargin;
11928                      }
11929                      if (strlen($strrest) > 0) {
11930                          // store the remaining string on the previous $key position

11931                          $this->newline = true;
11932                          if ($cell) {
11933                              if ($this->rtl) {
11934                                  $this->x -= $this->cMargin;
11935                              } else {
11936                                  $this->x += $this->cMargin;
11937                              }
11938                          }
11939                          if ($strrest == $dom[$key]['value']) {
11940                              // used to avoid infinite loop

11941                              ++$loop;
11942                          } else {
11943                              $loop = 0;
11944                          }
11945                          $dom[$key]['value'] = ltrim($strrest);
11946                          if ($loop < 3) {
11947                              --$key;
11948                          }
11949                      } else {
11950                          $loop = 0;
11951                      }
11952                  }
11953                  ++$key;
11954              } // end for each $key

11955              // align the last line

11956              if (isset($startlinex)) {
11957                  $yshift = $minstartliney - $startliney;
11958                  if (($yshift > 0) OR ($this->page > $startlinepage)) {
11959                      $yshift = 0;
11960                  }
11961                  if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
11962                      // the last line must be shifted to be aligned as requested

11963                      $linew = abs($this->endlinex - $startlinex);
11964                      $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
11965                      if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11966                          $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11967                          $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
11968                      } elseif (isset($opentagpos)) {
11969                          $midpos = $opentagpos;
11970                      } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11971                          $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11972                          $midpos = $this->footerpos[$startlinepage];
11973                      } else {
11974                          $midpos = 0;
11975                      }
11976                      if ($midpos > 0) {
11977                          $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
11978                          $pend = substr($this->getPageBuffer($startlinepage), $midpos);
11979                      } else {
11980                          $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
11981                          $pend = '';
11982                      }    
11983                      // calculate shifting amount

11984                      $tw = $w;
11985                      if ($this->lMargin != $prevlMargin) {
11986                          $tw += ($prevlMargin - $this->lMargin);
11987                      }
11988                      if ($this->rMargin != $prevrMargin) {
11989                          $tw += ($prevrMargin - $this->rMargin);
11990                      }
11991                      $mdiff = abs($tw - $linew);
11992                      if ($plalign == 'C') {
11993                          if ($this->rtl) {
11994                              $t_x = -($mdiff / 2);
11995                          } else {
11996                              $t_x = ($mdiff / 2);
11997                          }
11998                      } elseif (($plalign == 'R') AND (!$this->rtl)) {
11999                          // right alignment on LTR document

12000                          $t_x = $mdiff;
12001                      } elseif (($plalign == 'L') AND ($this->rtl)) {
12002                          // left alignment on RTL document

12003                          $t_x = -$mdiff;
12004                      } else {
12005                          $t_x = 0;
12006                      }
12007                      if (($t_x != 0) OR ($yshift < 0)) {
12008                          // shift the line

12009                          $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
12010                          $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
12011                          $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
12012                          // shift the annotations and links

12013                          if (isset($this->PageAnnots[$this->page])) {
12014                              foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
12015                                  if ($pak >= $pask) {
12016                                      $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
12017                                      $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
12018                                  }
12019                              }
12020                          }
12021                          $this->y -= $yshift;
12022                      }
12023                  }
12024              }
12025              if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
12026                  $this->Ln($this->lasth);
12027              }
12028              // restore previous values

12029              $this->setGraphicVars($gvars);
12030              if ($this->page > $prevPage) {
12031                  $this->lMargin = $this->pagedim[$this->page]['olm'];
12032                  $this->rMargin = $this->pagedim[$this->page]['orm'];
12033              }
12034              unset($dom);
12035          }
12036          
12037          /**

12038           * Process opening tags.

12039           * @param array $dom html dom array 

12040           * @param int $key current element id

12041           * @param boolean $cell if true add the default cMargin space to each new line (default false).

12042           * @access protected

12043           */
12044  		protected function openHTMLTagHandler(&$dom, $key, $cell=false) {
12045              $tag = $dom[$key];
12046              $parent = $dom[($dom[$key]['parent'])];
12047              $firstorlast = ($key == 1);
12048              // check for text direction attribute

12049              if (isset($tag['attribute']['dir'])) {
12050                  $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
12051              } else {
12052                  $this->tmprtl = false;
12053              }
12054              //Opening tag

12055              switch($tag['value']) {
12056                  case 'table': {
12057                      $cp = 0;
12058                      $cs = 0;
12059                      $dom[$key]['rowspans'] = array();
12060                      if (!$this->empty_string($dom[$key]['thead'])) {
12061                          // set table header

12062                          $this->thead = $dom[$key]['thead'];
12063                      }
12064                      if (isset($tag['attribute']['cellpadding'])) {
12065                          $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
12066                          $this->oldcMargin = $this->cMargin;
12067                          $this->cMargin = $cp;
12068                      }
12069                      if (isset($tag['attribute']['cellspacing'])) {
12070                          $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
12071                      }
12072                      $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth);
12073                      break;
12074                  }
12075                  case 'tr': {
12076                      // array of columns positions

12077                      $dom[$key]['cellpos'] = array();
12078                      break;
12079                  }
12080                  case 'hr': {
12081                      $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12082                      $this->htmlvspace = 0;
12083                      $wtmp = $this->w - $this->lMargin - $this->rMargin;
12084                      if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
12085                          $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px');
12086                      } else {
12087                          $hrWidth = $wtmp;
12088                      }
12089                      $x = $this->GetX();
12090                      $y = $this->GetY();
12091                      $prevlinewidth = $this->GetLineWidth();
12092                      $this->Line($x, $y, $x + $hrWidth, $y);
12093                      $this->SetLineWidth($prevlinewidth);
12094                      $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false);
12095                      break;
12096                  }
12097                  case 'a': {
12098                      if (array_key_exists('href', $tag['attribute'])) {
12099                          $this->HREF['url'] = $tag['attribute']['href'];
12100                      }
12101                      $this->HREF['color'] = $this->htmlLinkColorArray;
12102                      $this->HREF['style'] = $this->htmlLinkFontStyle;
12103                      if (array_key_exists('style', $tag['attribute'])) {
12104                          // get style attributes

12105                          preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
12106                          $astyle = array();
12107                          while (list($id, $name) = each($style_array[1])) {
12108                              $name = strtolower($name);
12109                              $astyle[$name] = trim($style_array[2][$id]);
12110                          }
12111                          if (isset($astyle['color'])) {
12112                              $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']);
12113                          }
12114                          if (isset($astyle['text-decoration'])) {
12115                              $this->HREF['style'] = '';
12116                              $decors = explode(' ', strtolower($astyle['text-decoration']));
12117                              foreach ($decors as $dec) {
12118                                  $dec = trim($dec);
12119                                  if (!$this->empty_string($dec)) {
12120                                      if ($dec{0} == 'u') {
12121                                          $this->HREF['style'] .= 'U';
12122                                      } elseif ($dec{0} == 'l') {
12123                                          $this->HREF['style'] .= 'D';
12124                                      }
12125                                  }
12126                              }
12127                          }
12128                      }        
12129                      break;
12130                  }
12131                  case 'img': {
12132                      if (isset($tag['attribute']['src'])) {
12133                          // replace relative path with real server path

12134                          if ($tag['attribute']['src'][0] == '/') {
12135                              $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
12136                          }
12137                          $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
12138                          $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
12139                          if (!isset($tag['attribute']['width'])) {
12140                              $tag['attribute']['width'] = 0;
12141                          }
12142                          if (!isset($tag['attribute']['height'])) {
12143                              $tag['attribute']['height'] = 0;
12144                          }
12145                          //if (!isset($tag['attribute']['align'])) {

12146                              // the only alignment supported is "bottom"

12147                              // further development is required for other modes.

12148                              $tag['attribute']['align'] = 'bottom';
12149                          //} 

12150                          switch($tag['attribute']['align']) {
12151                              case 'top': {
12152                                  $align = 'T';
12153                                  break;
12154                              }
12155                              case 'middle': {
12156                                  $align = 'M';
12157                                  break;
12158                              }
12159                              case 'bottom': {
12160                                  $align = 'B';
12161                                  break;
12162                              }
12163                              default: {
12164                                  $align = 'B';
12165                                  break;
12166                              }
12167                          }
12168                          $fileinfo = pathinfo($tag['attribute']['src']);
12169                          if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
12170                              $type = strtolower($fileinfo['extension']);
12171                          }
12172                          $prevy = $this->y;
12173                          $xpos = $this->GetX();
12174                          if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) {
12175                              if ($this->rtl) {
12176                                  $xpos += $this->GetStringWidth(' ');
12177                              } else {
12178                                  $xpos -= $this->GetStringWidth(' ');
12179                              }
12180                          }
12181                          $imglink = '';
12182                          if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
12183                              $imglink = $this->HREF['url'];
12184                              if ($imglink{0} == '#') {
12185                                  // convert url to internal link

12186                                  $page = intval(substr($imglink, 1));
12187                                  $imglink = $this->AddLink();
12188                                  $this->SetLink($imglink, 0, $page);
12189                              }
12190                          }
12191                          $border = 0;
12192                          if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) {
12193                              // currently only support 1 (frame) or a combination of 'LTRB'

12194                              $border = $tag['attribute']['border'];
12195                          }
12196                          $iw = '';
12197                          if (isset($tag['attribute']['width'])) {
12198                              $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false);
12199                          }
12200                          $ih = '';
12201                          if (isset($tag['attribute']['height'])) {
12202                              $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false);
12203                          }
12204                          if (($type == 'eps') OR ($type == 'ai')) {
12205                              $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border);
12206                          } else {
12207                              $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border);
12208                          }
12209                          switch($align) {
12210                              case 'T': {
12211                                  $this->y = $prevy;
12212                                  break;
12213                              }
12214                              case 'M': {
12215                                  $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
12216                                  break;
12217                              }
12218                              case 'B': {
12219                                  $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
12220                                  break;
12221                              }
12222                          }
12223                      }
12224                      break;
12225                  }
12226                  case 'dl': {
12227                      ++$this->listnum;
12228                      $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12229                      break;
12230                  }
12231                  case 'dt': {
12232                      $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12233                      break;
12234                  }
12235                  case 'dd': {
12236                      if ($this->rtl) {
12237                          $this->rMargin += $this->listindent;
12238                      } else {
12239                          $this->lMargin += $this->listindent;
12240                      }
12241                      $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12242                      break;
12243                  }
12244                  case 'ul':
12245                  case 'ol': {
12246                      $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12247                      $this->htmlvspace = 0;
12248                      ++$this->listnum;
12249                      if ($tag['value'] == 'ol') {
12250                          $this->listordered[$this->listnum] = true;
12251                      } else {
12252                          $this->listordered[$this->listnum] = false;
12253                      }
12254                      if (isset($tag['attribute']['start'])) {
12255                          $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
12256                      } else {
12257                          $this->listcount[$this->listnum] = 0;
12258                      }
12259                      if ($this->rtl) {
12260                          $this->rMargin += $this->listindent;
12261                      } else {
12262                          $this->lMargin += $this->listindent;
12263                      }
12264                      $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12265                      $this->htmlvspace = 0;
12266                      break;
12267                  }
12268                  case 'li': {
12269                      $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12270                      if ($this->listordered[$this->listnum]) {
12271                          // ordered item

12272                          if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12273                              $this->lispacer = $parent['attribute']['type'];
12274                          } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12275                              $this->lispacer = $parent['listtype'];
12276                          } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12277                              $this->lispacer = $this->lisymbol;
12278                          } else {
12279                              $this->lispacer = '#';
12280                          }
12281                          ++$this->listcount[$this->listnum];
12282                          if (isset($tag['attribute']['value'])) {
12283                              $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
12284                          }
12285                      } else {
12286                          // unordered item

12287                          if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12288                              $this->lispacer = $parent['attribute']['type'];
12289                          } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12290                              $this->lispacer = $parent['listtype'];
12291                          } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12292                              $this->lispacer = $this->lisymbol;
12293                          } else {
12294                              $this->lispacer = '!';
12295                          }
12296                      }
12297                      break;
12298                  }
12299                  case 'blockquote': {
12300                      if ($this->rtl) {
12301                          $this->rMargin += $this->listindent;
12302                      } else {
12303                          $this->lMargin += $this->listindent;
12304                      }
12305                      $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12306                      break;
12307                  }
12308                  case 'br': {
12309                      $this->Ln('', $cell);
12310                      break;
12311                  }
12312                  case 'div': {
12313                      $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12314                      break;
12315                  }
12316                  case 'p': {
12317                      $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12318                      break;
12319                  }
12320                  case 'pre': {
12321                      $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12322                      $this->premode = true;
12323                      break;
12324                  }
12325                  case 'sup': {
12326                      $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
12327                      break;
12328                  }
12329                  case 'sub': {
12330                      $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
12331                      break;
12332                  }
12333                  case 'h1': 
12334                  case 'h2': 
12335                  case 'h3': 
12336                  case 'h4': 
12337                  case 'h5': 
12338                  case 'h6': {
12339                      $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false);
12340                      break;
12341                  }
12342                  case 'tcpdf': {
12343                      // NOT HTML: used to call TCPDF methods

12344                      if (isset($tag['attribute']['method'])) {
12345                          $tcpdf_method = $tag['attribute']['method'];
12346                          if (method_exists($this, $tcpdf_method)) {
12347                              if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
12348                                  eval('$params = array('.$tag['attribute']['params'].');');
12349                                  call_user_func_array(array($this, $tcpdf_method), $params);
12350                              } else {
12351                                  $this->$tcpdf_method();
12352                              }
12353                              $this->newline = true;
12354                          }
12355                      }
12356                  }
12357                  default: {
12358                      break;
12359                  }
12360              }
12361          }
12362          
12363          /**

12364           * Process closing tags.

12365           * @param array $dom html dom array 

12366           * @param int $key current element id

12367           * @param boolean $cell if true add the default cMargin space to each new line (default false).

12368           * @access protected

12369           */
12370  		protected function closeHTMLTagHandler(&$dom, $key, $cell=false) {
12371              $tag = $dom[$key];
12372              $parent = $dom[($dom[$key]['parent'])];
12373              $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
12374              //Closing tag

12375              switch($tag['value']) {
12376                  case 'tr': {
12377                      $table_el = $dom[($dom[$key]['parent'])]['parent'];
12378                      if(!isset($parent['endy'])) {
12379                          $dom[($dom[$key]['parent'])]['endy'] = $this->y;
12380                          $parent['endy'] = $this->y;
12381                      }
12382                      if(!isset($parent['endpage'])) {
12383                          $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
12384                          $parent['endpage'] = $this->page;
12385                      }
12386                      // update row-spanned cells

12387                      if (isset($dom[$table_el]['rowspans'])) {
12388                          foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12389                              $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
12390                              if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12391                                  if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) {
12392                                      $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
12393                                  } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) {
12394                                      $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12395                                      $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12396                                  }
12397                              }
12398                          }
12399                          // report new endy and endpage to the rowspanned cells

12400                          foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12401                              if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12402                                  $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
12403                                  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12404                                  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
12405                                  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12406                              }
12407                          }
12408                          // update remaining rowspanned cells

12409                          foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12410                              if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12411                                  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
12412                                  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
12413                              }
12414                          }
12415                      }
12416                      $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
12417                      $this->y = $dom[($dom[$key]['parent'])]['endy'];                    
12418                      if (isset($dom[$table_el]['attribute']['cellspacing'])) {
12419                          $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
12420                          $this->y += $cellspacing;
12421                      }                
12422                      $this->Ln(0, $cell);
12423                      $this->x = $parent['startx'];
12424                      // account for booklet mode

12425                      if ($this->page > $parent['startpage']) {
12426                          if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
12427                              $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
12428                          } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
12429                              $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
12430                          }
12431                      }
12432                      break;
12433                  }
12434                  case 'table': {
12435                      // draw borders

12436                      $table_el = $parent;
12437                      if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) 
12438                          OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
12439                              $border = 1;
12440                      } else {
12441                          $border = 0;
12442                      }
12443                      // fix bottom line alignment of last line before page break

12444                      foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
12445                          // update row-spanned cells

12446                          if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12447                              foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12448                                  if ($trwsp['trid'] == $trkey) {
12449                                      $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
12450                                  }
12451                                  if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
12452                                      $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
12453                                  }
12454                              }
12455                          }
12456                          if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
12457                              $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
12458                              $dom[$prevtrkey]['endy'] = $pgendy;
12459                              // update row-spanned cells

12460                              if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12461                                  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12462                                      if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
12463                                          $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
12464                                          $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
12465                                      }
12466                                  }
12467                              }
12468                          }
12469                          $prevtrkey = $trkey;
12470                          $table_el = $dom[($dom[$key]['parent'])];
12471                      }
12472                      // for each row

12473                      foreach ($table_el['trids'] as $j => $trkey) {
12474                          $parent = $dom[$trkey];
12475                          // for each cell on the row

12476                          foreach ($parent['cellpos'] as $k => $cellpos) {
12477                              if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
12478                                  $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
12479                                  $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
12480                                  $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
12481                                  $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
12482                                  $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
12483                              } else {
12484                                  $endy = $parent['endy'];
12485                                  $startpage = $parent['startpage'];
12486                                  $endpage = $parent['endpage'];
12487                              }
12488                              if ($endpage > $startpage) {
12489                                  // design borders around HTML cells.

12490                                  for ($page=$startpage; $page <= $endpage; ++$page) {
12491                                      $this->setPage($page);
12492                                      if ($page == $startpage) {
12493                                          $this->y = $parent['starty']; // put cursor at the beginning of row on the first page

12494                                          $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin();
12495                                          $cborder = $this->getBorderMode($border, $position='start');
12496                                      } elseif ($page == $endpage) {
12497                                          $this->y = $this->tMargin; // put cursor at the beginning of last page

12498                                          $ch = $endy - $this->tMargin;
12499                                          $cborder = $this->getBorderMode($border, $position='end');
12500                                      } else {
12501                                          $this->y = $this->tMargin; // put cursor at the beginning of the current page

12502                                          $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
12503                                          $cborder = $this->getBorderMode($border, $position='middle');
12504                                      }
12505                                      if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12506                                          $this->SetFillColorArray($cellpos['bgcolor']);
12507                                          $fill = true;
12508                                      } else {
12509                                          $fill = false;
12510                                      }
12511                                      $cw = abs($cellpos['endx'] - $cellpos['startx']);
12512                                      $this->x = $cellpos['startx'];
12513                                      // account for margin changes

12514                                      if ($page > $startpage) {
12515                                          if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
12516                                              $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
12517                                          } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) {
12518                                              $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
12519                                          }
12520                                      }
12521                                      // design a cell around the text

12522                                      $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true);
12523                                      if ($cborder OR $fill) {
12524                                          $pagebuff = $this->getPageBuffer($this->page);
12525                                          $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
12526                                          $pend = substr($pagebuff, $this->intmrk[$this->page]);
12527                                          $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12528                                          $this->intmrk[$this->page] += strlen($ccode."\n");
12529                                      }
12530                                  }
12531                              } else {
12532                                  $this->setPage($startpage);
12533                                  if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12534                                      $this->SetFillColorArray($cellpos['bgcolor']);
12535                                      $fill = true;
12536                                  } else {
12537                                      $fill = false;
12538                                  }
12539                                  $this->x = $cellpos['startx'];
12540                                  $this->y = $parent['starty'];
12541                                  $cw = abs($cellpos['endx'] - $cellpos['startx']);
12542                                  $ch = $endy - $parent['starty'];
12543                                  // design a cell around the text

12544                                  $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true);
12545                                  if ($border OR $fill) {
12546                                      if (end($this->transfmrk[$this->page]) !== false) {
12547                                          $pagemarkkey = key($this->transfmrk[$this->page]);
12548                                          $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
12549                                      } elseif ($this->InFooter) {
12550                                          $pagemark = &$this->footerpos[$this->page];
12551                                      } else {
12552                                          $pagemark = &$this->intmrk[$this->page];
12553                                      }
12554                                      $pagebuff = $this->getPageBuffer($this->page);
12555                                      $pstart = substr($pagebuff, 0, $pagemark);
12556                                      $pend = substr($pagebuff, $pagemark);
12557                                      $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12558                                      $pagemark += strlen($ccode."\n");
12559                                  }                    
12560                              }
12561                          }                    
12562                          if (isset($table_el['attribute']['cellspacing'])) {
12563                              $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
12564                              $this->y += $cellspacing;
12565                          }                
12566                          $this->Ln(0, $cell);
12567                          $this->x = $parent['startx'];
12568                          if ($endpage > $startpage) {
12569                              if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
12570                                  $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
12571                              } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
12572                                  $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
12573                              }
12574                          }
12575                      }
12576                      if (isset($parent['cellpadding'])) {
12577                          $this->cMargin = $this->oldcMargin;
12578                      }
12579                      $this->lasth = $this->FontSize * $this->cell_height_ratio;
12580                      if (!$this->empty_string($this->theadMargin)) {
12581                          // restore top margin

12582                          $this->tMargin = $this->theadMargin;
12583                          $this->pagedim[$this->page]['tm'] = $this->theadMargin;
12584                      }
12585                      // reset table header

12586                      $this->thead = '';
12587                      $this->theadMargin = '';
12588                      break;
12589                  }
12590                  case 'a': {
12591                      $this->HREF = '';
12592                      break;
12593                  }
12594                  case 'sup': {
12595                      $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
12596                      break;
12597                  }
12598                  case 'sub': {
12599                      $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
12600                      break;
12601                  }
12602                  case 'div': {
12603                      $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12604                      break;
12605                  }
12606                  case 'blockquote': {
12607                      if ($this->rtl) {
12608                          $this->rMargin -= $this->listindent;
12609                      } else {
12610                          $this->lMargin -= $this->listindent;
12611                      }
12612                      $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12613                      break;
12614                  }
12615                  case 'p': {
12616                      $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12617                      break;
12618                  }
12619                  case 'pre': {
12620                      $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12621                      $this->premode = false;
12622                      break;
12623                  }
12624                  case 'dl': {
12625                      --$this->listnum;
12626                      if ($this->listnum <= 0) {
12627                          $this->listnum = 0;
12628                          $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12629                      }
12630                      break;
12631                  }
12632                  case 'dt': {
12633                      $this->lispacer = '';
12634                      $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12635                      break;
12636                  }
12637                  case 'dd': {
12638                      $this->lispacer = '';
12639                      if ($this->rtl) {
12640                          $this->rMargin -= $this->listindent;
12641                      } else {
12642                          $this->lMargin -= $this->listindent;
12643                      }
12644                      $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12645                      break;
12646                  }
12647                  case 'ul':
12648                  case 'ol': {
12649                      --$this->listnum;
12650                      $this->lispacer = '';
12651                      if ($this->rtl) {
12652                          $this->rMargin -= $this->listindent;
12653                      } else {
12654                          $this->lMargin -= $this->listindent;
12655                      }
12656                      if ($this->listnum <= 0) {
12657                          $this->listnum = 0;
12658                          $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12659                      }
12660                      $this->lasth = $this->FontSize * $this->cell_height_ratio;
12661                      break;
12662                  }
12663                  case 'li': {
12664                      $this->lispacer = '';
12665                      $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12666                      break;
12667                  }
12668                  case 'h1': 
12669                  case 'h2': 
12670                  case 'h3': 
12671                  case 'h4': 
12672                  case 'h5': 
12673                  case 'h6': {
12674                      $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true);
12675                      break;
12676                  }
12677                  default : {
12678                      break;
12679                  }
12680              }
12681              $this->tmprtl = false;
12682          }
12683          
12684          /**

12685           * Add vertical spaces if needed.

12686           * @param int $n number of spaces to add

12687           * @param boolean $cell if true add the default cMargin space to each new line (default false).

12688           * @param string $h The height of the break. By default, the value equals the height of the last printed cell.

12689           * @param boolean $firstorlast if true do not print additional empty lines.

12690           * @param string $tag HTML tag to which this space will be applied

12691           * @param boolean $closing true if this space will be applied to a closing tag, false otherwise

12692           * @access protected

12693           */
12694  		protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) {
12695              if ($firstorlast) {
12696                  $this->Ln(0, $cell);
12697                  $this->htmlvspace = 0;
12698                  return;
12699              }
12700              if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) {
12701                  $n = $this->tagvspaces[$tag][intval($closing)]['n'];
12702              }
12703              if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) {
12704                  $h = $this->tagvspaces[$tag][intval($closing)]['h'];
12705              }
12706              if (is_string($h)) {
12707                  $vsize = $n * $this->lasth;
12708              } else {
12709                  $vsize = $n * $h;
12710              }
12711              if ($vsize > $this->htmlvspace) {
12712                  $this->Ln(($vsize - $this->htmlvspace), $cell);
12713                  $this->htmlvspace = $vsize;
12714              }
12715          }
12716          
12717          /**

12718           * Set the default bullet to be used as LI bullet symbol

12719           * @param string $symbol character or string to be used (legal values are: '' = automatic, '!' = auto bullet, '#' = auto numbering, 'disc', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek')

12720           * @access public

12721           * @since 4.0.028 (2008-09-26)

12722           */
12723  		public function setLIsymbol($symbol='!') {
12724              $symbol = strtolower($symbol);
12725              switch ($symbol) {
12726                  case '!' :
12727                  case '#' :
12728                  case 'disc' :
12729                  case 'disc' :
12730                  case 'circle' :
12731                  case 'square' :
12732                  case '1':
12733                  case 'decimal':
12734                  case 'decimal-leading-zero':
12735                  case 'i':
12736                  case 'lower-roman':
12737                  case 'I':
12738                  case 'upper-roman':
12739                  case 'a':
12740                  case 'lower-alpha':
12741                  case 'lower-latin':
12742                  case 'A':
12743                  case 'upper-alpha':
12744                  case 'upper-latin':
12745                  case 'lower-greek': {
12746                      $this->lisymbol = $symbol;
12747                      break;
12748                  }
12749                  default : {
12750                      $this->lisymbol = '';
12751                  }
12752              }
12753          }
12754          
12755          /**

12756          * Set the booklet mode for double-sided pages.

12757          * @param boolean $booklet true set the booklet mode on, fals eotherwise.

12758          * @param float $inner Inner page margin.

12759          * @param float $outer Outer page margin.

12760          * @access public

12761          * @since 4.2.000 (2008-10-29)

12762          */
12763  		public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
12764              $this->booklet = $booklet;
12765              if ($inner >= 0) {
12766                  $this->lMargin = $inner;
12767              }
12768              if ($outer >= 0) {
12769                  $this->rMargin = $outer;
12770              }
12771          }
12772          
12773          /**

12774          * Swap the left and right margins.

12775          * @param boolean $reverse if true swap left and right margins.

12776          * @access protected

12777          * @since 4.2.000 (2008-10-29)

12778          */
12779  		protected function swapMargins($reverse=true) {
12780              if ($reverse) {
12781                  // swap left and right margins

12782                  $mtemp = $this->original_lMargin;
12783                  $this->original_lMargin = $this->original_rMargin;
12784                  $this->original_rMargin = $mtemp;
12785                  $deltam = $this->original_lMargin - $this->original_rMargin;
12786                  $this->lMargin += $deltam;
12787                  $this->rMargin -= $deltam;
12788              }
12789          }
12790  
12791          /**

12792          * Set the vertical spaces for HTML tags.

12793          * The array must have the following structure (example):

12794          * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));

12795          * The first array level contains the tag names,

12796          * the second level contains 0 for opening tags or 1 for closing tags,

12797          * the third level contains the vertical space unit (h) and the number spaces to add (n).

12798          * If the h parameter is not specified, default values are used.

12799          * @param array $tagvs array of tags and relative vertical spaces.

12800          * @access public

12801          * @since 4.2.001 (2008-10-30)

12802          */
12803  		public function setHtmlVSpace($tagvs) {
12804              $this->tagvspaces = $tagvs;
12805          }
12806  
12807          /**

12808          * Set custom width for list indentation.

12809          * @param float $width width of the indentation. Use negative value to disable it.

12810          * @access public

12811          * @since 4.2.007 (2008-11-12)

12812          */
12813  		public function setListIndentWidth($width) {
12814              return $this->customlistindent = floatval($width);
12815          }
12816  
12817          /**

12818          * Set the top/bottom cell sides to be open or closed when the cell cross the page.

12819          * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page.

12820          * @access public

12821          * @since 4.2.010 (2008-11-14)

12822          */
12823  		public function setOpenCell($isopen) {
12824              $this->opencell = $isopen;
12825          }
12826  
12827          /**

12828          * Set the color and font style for HTML links.

12829          * @param array $color RGB array of colors

12830          * @param string $fontstyle additional font styles to add

12831          * @access public

12832          * @since 4.4.003 (2008-12-09)

12833          */
12834  		public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
12835              $this->htmlLinkColorArray = $color;
12836              $this->htmlLinkFontStyle = $fontstyle;
12837          }
12838  
12839          /**

12840          * convert html string containing value and unit of measure to user's units or points.

12841          * @param string $htmlval string containing values and unit

12842          * @param string $refsize reference value in points

12843          * @param string $defaultunit default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).

12844          * @param boolean $point if true returns points, otherwise returns value in user's units

12845          * @return float value in user's unit or point if $points=true

12846          * @access public

12847          * @since 4.4.004 (2008-12-10)

12848          */
12849          public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
12850              $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
12851              $retval = 0;
12852              $value = 0;
12853              $unit = 'px';
12854              $k = $this->k;
12855              if ($points) {
12856                  $k = 1;
12857              }
12858              if (in_array($defaultunit, $supportedunits)) {
12859                  $unit = $defaultunit;
12860              }
12861              if (is_numeric($htmlval)) {
12862                  $value = floatval($htmlval);
12863              } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) {
12864                  $value = floatval($mnum[1]);
12865                  if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
12866                      if (in_array($munit[1], $supportedunits)) {
12867                          $unit = $munit[1];
12868                      }
12869                  }
12870              }
12871              switch ($unit) {
12872                  // percentage

12873                  case '%': {
12874                      $retval = (($value * $refsize) / 100);
12875                      break;
12876                  }
12877                  // relative-size

12878                  case 'em': {
12879                      $retval = ($value * $refsize);
12880                      break;
12881                  }
12882                  case 'ex': {
12883                      $retval = $value * ($refsize / 2);
12884                      break;
12885                  }
12886                  // absolute-size

12887                  case 'in': {
12888                      $retval = ($value * $this->dpi) / $k;
12889                      break;
12890                  }
12891                  case 'cm': {
12892                      $retval = ($value / 2.54 * $this->dpi) / $k;
12893                      break;
12894                  }
12895                  case 'mm': {
12896                      $retval = ($value / 25.4 * $this->dpi) / $k;
12897                      break;
12898                  }
12899                  case 'pc': {
12900                      // one pica is 12 points

12901                      $retval = ($value * 12) / $k;
12902                      break;
12903                  }
12904                  case 'pt': {
12905                      $retval = $value / $k;
12906                      break;
12907                  }
12908                  case 'px': {
12909                      $retval = $this->pixelsToUnits($value);
12910                      break;
12911                  }
12912              }
12913              return $retval;
12914          }
12915  
12916          /**

12917          * Returns the Roman representation of an integer number

12918          * @param int number to convert

12919          * @return string roman representation of the specified number

12920          * @access public

12921          * @since 4.4.004 (2008-12-10)

12922          */
12923  		public function intToRoman($number) {
12924              $roman = '';
12925              while ($number >= 1000) {
12926                  $roman .= 'M';
12927                  $number -= 1000;
12928              }
12929              while ($number >= 900) {
12930                  $roman .= 'CM';
12931                  $number -= 900;
12932              }
12933              while ($number >= 500) {
12934                  $roman .= 'D';
12935                  $number -= 500;
12936              }
12937              while ($number >= 400) {
12938                  $roman .= 'CD';
12939                  $number -= 400;
12940              }
12941              while ($number >= 100) {
12942                  $roman .= 'C';
12943                  $number -= 100;
12944              }
12945              while ($number >= 90) {
12946              $roman .= 'XC';
12947              $number -= 90;
12948              }
12949              while ($number >= 50) {
12950                  $roman .= 'L';
12951                  $number -= 50;
12952              }
12953              while ($number >= 40) {
12954                  $roman .= 'XL';
12955                  $number -= 40;
12956              }
12957              while ($number >= 10) {
12958              $roman .= 'X';
12959              $number -= 10;
12960              }
12961              while ($number >= 9) {
12962                  $roman .= 'IX';
12963                  $number -= 9;
12964              }
12965              while ($number >= 5) {
12966                  $roman .= 'V';
12967                  $number -= 5;
12968              }
12969              while ($number >= 4) {
12970              $roman .= 'IV';
12971              $number -= 4;
12972              }
12973              while ($number >= 1) {
12974                  $roman .= 'I';
12975                  --$number;
12976              }
12977              return $roman;
12978          }
12979  
12980          /**

12981          * Output an HTML list bullet or ordered item symbol

12982          * @param int $listdepth list nesting level

12983          * @param string $listtype type of list

12984          * @param float $size current font size

12985          * @access protected

12986          * @since 4.4.004 (2008-12-10)

12987          */
12988  		protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
12989              $size /= $this->k;
12990              $fill = '';
12991              $color = $this->fgcolor;
12992              $width = 0;
12993              $textitem = '';
12994              $tmpx = $this->x;        
12995              $lspace = $this->GetStringWidth('  ');
12996              if ($listtype == '!') {
12997                  // set default list type for unordered list

12998                  $deftypes = array('disc', 'circle', 'square');
12999                  $listtype = $deftypes[($listdepth - 1) % 3];
13000              } elseif ($listtype == '#') {
13001                  // set default list type for ordered list

13002                  $listtype = 'decimal';
13003              }
13004              switch ($listtype) {
13005                  // unordered types

13006                  case 'none': {
13007                      break;
13008                  }
13009                  case 'disc': {
13010                      $fill = 'F';
13011                  }
13012                  case 'circle': {
13013                      $fill .= 'D';
13014                      $r = $size / 6;
13015                      $lspace += (2 * $r);
13016                      if ($this->rtl) {
13017                          $this->x += $lspace;
13018                      } else {
13019                          $this->x -= $lspace;
13020                      }
13021                      $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
13022                      break;
13023                  }
13024                  case 'square': {
13025                      $l = $size / 3;
13026                      $lspace += $l;
13027                      if ($this->rtl) {
13028                          $this->x += $lspace;
13029                      } else {
13030                          $this->x -= $lspace;
13031                      }
13032                      $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color);
13033                      break;
13034                  }
13035                  // ordered types

13036  
13037                  // $this->listcount[$this->listnum];

13038                  // $textitem

13039                  case '1':
13040                  case 'decimal': {
13041                      $textitem = $this->listcount[$this->listnum];
13042                      break;
13043                  }
13044                  case 'decimal-leading-zero': {
13045                      $textitem = sprintf("%02d", $this->listcount[$this->listnum]);
13046                      break;
13047                  }
13048                  case 'i':
13049                  case 'lower-roman': {
13050                      $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
13051                      break;
13052                  }
13053                  case 'I':
13054                  case 'upper-roman': {
13055                      $textitem = $this->intToRoman($this->listcount[$this->listnum]);
13056                      break;
13057                  }
13058                  case 'a':
13059                  case 'lower-alpha':
13060                  case 'lower-latin': {
13061                      $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
13062                      break;
13063                  }
13064                  case 'A':
13065                  case 'upper-alpha':
13066                  case 'upper-latin': {
13067                      $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
13068                      break;
13069                  }
13070                  case 'lower-greek': {
13071                      $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
13072                      break;
13073                  }
13074                  /*

13075                  // Types to be implemented (special handling)

13076                  case 'hebrew': {

13077                      break;

13078                  }

13079                  case 'armenian': {

13080                      break;

13081                  }

13082                  case 'georgian': {

13083                      break;

13084                  }

13085                  case 'cjk-ideographic': {

13086                      break;

13087                  }

13088                  case 'hiragana': {

13089                      break;

13090                  }

13091                  case 'katakana': {

13092                      break;

13093                  }

13094                  case 'hiragana-iroha': {

13095                      break;

13096                  }

13097                  case 'katakana-iroha': {

13098                      break;

13099                  }

13100                  */
13101                  default: {
13102                      $textitem = $this->listcount[$this->listnum];
13103                  }
13104              }
13105              if (!$this->empty_string($textitem)) {
13106                  // print ordered item

13107                  if ($this->rtl) {
13108                      $textitem = '.'.$textitem;
13109                  } else {
13110                      $textitem = $textitem.'.';
13111                  }
13112                  $lspace += $this->GetStringWidth($textitem);
13113                  if ($this->rtl) {
13114                      $this->x += $lspace;
13115                  } else {
13116                      $this->x -= $lspace;
13117                  }
13118                  $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
13119              }
13120              $this->x = $tmpx;
13121              $this->lispacer = '';
13122          }
13123  
13124          /**

13125          * Returns current graphic variables as array.

13126          * @return array graphic variables

13127          * @access protected

13128          * @since 4.2.010 (2008-11-14)

13129          */
13130  		protected function getGraphicVars() {
13131              $grapvars = array(
13132                  'FontFamily' => $this->FontFamily,
13133                  'FontStyle' => $this->FontStyle,
13134                  'FontSizePt' => $this->FontSizePt,
13135                  'rMargin' => $this->rMargin,
13136                  'lMargin' => $this->lMargin,
13137                  'cMargin' => $this->cMargin,
13138                  'LineWidth' => $this->LineWidth,
13139                  'linestyleWidth' => $this->linestyleWidth,
13140                  'linestyleCap' => $this->linestyleCap,
13141                  'linestyleJoin' => $this->linestyleJoin,
13142                  'linestyleDash' => $this->linestyleDash,
13143                  'DrawColor' => $this->DrawColor,
13144                  'FillColor' => $this->FillColor,
13145                  'TextColor' => $this->TextColor,
13146                  'ColorFlag' => $this->ColorFlag,
13147                  'bgcolor' => $this->bgcolor,
13148                  'fgcolor' => $this->fgcolor,
13149                  'htmlvspace' => $this->htmlvspace,
13150                  'lasth' => $this->lasth
13151                  );
13152              return $grapvars;
13153          }
13154  
13155          /**

13156          * Set graphic variables.

13157          * @param $gvars array graphic variables

13158          * @access protected

13159          * @since 4.2.010 (2008-11-14)

13160          */
13161  		protected function setGraphicVars($gvars) {
13162              $this->FontFamily = $gvars['FontFamily'];
13163              $this->FontStyle = $gvars['FontStyle'];
13164              $this->FontSizePt = $gvars['FontSizePt'];
13165              $this->rMargin = $gvars['rMargin'];
13166              $this->lMargin = $gvars['lMargin'];
13167              $this->cMargin = $gvars['cMargin'];
13168              $this->LineWidth = $gvars['LineWidth'];
13169              $this->linestyleWidth = $gvars['linestyleWidth'];
13170              $this->linestyleCap = $gvars['linestyleCap'];
13171              $this->linestyleJoin = $gvars['linestyleJoin'];
13172              $this->linestyleDash = $gvars['linestyleDash'];
13173              $this->DrawColor = $gvars['DrawColor'];
13174              $this->FillColor = $gvars['FillColor'];
13175              $this->TextColor = $gvars['TextColor'];
13176              $this->ColorFlag = $gvars['ColorFlag'];
13177              $this->bgcolor = $gvars['bgcolor'];
13178              $this->fgcolor = $gvars['fgcolor'];
13179              $this->htmlvspace = $gvars['htmlvspace'];
13180              //$this->lasth = $gvars['lasth'];

13181              $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
13182              if (!$this->empty_string($this->FontFamily)) {
13183                  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
13184              }
13185          }
13186  
13187          /**

13188          * Returns a temporary filename for caching object on filesystem.

13189          * @param string $prefix prefix to add to filename

13190          * return string filename.

13191          * @access protected

13192          * @since 4.5.000 (2008-12-31)

13193          */
13194  		protected function getObjFilename($name) {
13195              return tempnam(K_PATH_CACHE, $name.'_');
13196          }
13197  
13198          /**

13199          * Writes data to a temporary file on filesystem.

13200          * @param string $file file name

13201          * @param mixed $data data to write on file

13202          * @param boolean $append if true append data, false replace.

13203          * @access protected

13204          * @since 4.5.000 (2008-12-31)

13205          */
13206  		protected function writeDiskCache($filename, $data, $append=false) {
13207              if ($append) {
13208                  $fmode = 'ab+';
13209              } else {
13210                  $fmode = 'wb+';
13211              }
13212              $f = @fopen($filename, $fmode);
13213              if (!$f) {
13214                  $this->Error('Unable to write cache file: '.$filename);
13215              } else {
13216                  fwrite($f, $data);
13217                  fclose($f);
13218              }
13219              // update file lenght (needed for transactions)

13220              if (!isset($this->cache_file_lenght['_'.$filename])) {
13221                  $this->cache_file_lenght['_'.$filename] = strlen($data);
13222              } else {
13223                  $this->cache_file_lenght['_'.$filename] += strlen($data);
13224              }
13225          }
13226  
13227          /**

13228          * Read data from a temporary file on filesystem.

13229          * @param string $file file name

13230          * @return mixed retrieved data

13231          * @access protected

13232          * @since 4.5.000 (2008-12-31)

13233          */
13234  		protected function readDiskCache($filename) {
13235              return file_get_contents($filename);
13236          }
13237  
13238          /**

13239          * Set buffer content (always append data).

13240          * @param string $data data

13241          * @access protected

13242          * @since 4.5.000 (2009-01-02)

13243          */
13244  		protected function setBuffer($data) {
13245              $this->bufferlen += strlen($data);
13246              if ($this->diskcache) {
13247                  if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
13248                      $this->buffer = $this->getObjFilename('buffer');
13249                  }
13250                  $this->writeDiskCache($this->buffer, $data, true);
13251              } else {
13252                  $this->buffer .= $data;
13253              }
13254          }
13255  
13256          /**

13257          * Get buffer content.

13258          * @return string buffer content

13259          * @access protected

13260          * @since 4.5.000 (2009-01-02)

13261          */
13262  		protected function getBuffer() {
13263              if ($this->diskcache) {
13264                  return $this->readDiskCache($this->buffer);
13265              } else {
13266                  return $this->buffer;
13267              }
13268          }
13269  
13270          /**

13271          * Set page buffer content.

13272          * @param int $page page number

13273          * @param string $data page data

13274          * @param boolean $append if true append data, false replace.

13275          * @access protected

13276          * @since 4.5.000 (2008-12-31)

13277          */
13278  		protected function setPageBuffer($page, $data, $append=false) {
13279              if ($this->diskcache) {
13280                  if (!isset($this->pages[$page])) {
13281                      $this->pages[$page] = $this->getObjFilename('page'.$page);
13282                  }
13283                  $this->writeDiskCache($this->pages[$page], $data, $append);
13284              } else {
13285                  if ($append) {
13286                      $this->pages[$page] .= $data;
13287                  } else {
13288                      $this->pages[$page] = $data;
13289                  }
13290              }
13291              if ($append AND isset($this->pagelen[$page])) {
13292                  $this->pagelen[$page] += strlen($data);
13293              } else {
13294                  $this->pagelen[$page] = strlen($data);
13295              }
13296          }
13297  
13298          /**

13299          * Get page buffer content.

13300          * @param int $page page number

13301          * @return string page buffer content or false in case of error

13302          * @access protected

13303          * @since 4.5.000 (2008-12-31)

13304          */
13305  		protected function getPageBuffer($page) {
13306              if ($this->diskcache) {
13307                  return $this->readDiskCache($this->pages[$page]);
13308              } elseif (isset($this->pages[$page])) {
13309                  return $this->pages[$page];
13310              }
13311              return false;
13312          }
13313  
13314          /**

13315          * Set image buffer content.

13316          * @param string $image image key

13317          * @param array $data image data

13318          * @access protected

13319          * @since 4.5.000 (2008-12-31)

13320          */
13321  		protected function setImageBuffer($image, $data) {
13322              if ($this->diskcache) {
13323                  if (!isset($this->images[$image])) {
13324                      $this->images[$image] = $this->getObjFilename('image'.$image);
13325                  }
13326                  $this->writeDiskCache($this->images[$image], serialize($data));
13327              } else {
13328                  $this->images[$image] = $data;
13329              }
13330              if (!in_array($image, $this->imagekeys)) {
13331                  $this->imagekeys[] = $image;
13332              }
13333              ++$this->numimages;
13334          }
13335  
13336          /**

13337          * Set image buffer content.

13338          * @param string $image image key

13339          * @param string $key image sub-key

13340          * @param array $data image data

13341          * @access protected

13342          * @since 4.5.000 (2008-12-31)

13343          */
13344  		protected function setImageSubBuffer($image, $key, $data) {
13345              if (!isset($this->images[$image])) {
13346                  $this->setImageBuffer($image, array());
13347              }
13348              if ($this->diskcache) {
13349                  $tmpimg = $this->getImageBuffer($image);
13350                  $tmpimg[$key] = $data;
13351                  $this->writeDiskCache($this->images[$image], serialize($tmpimg));
13352              } else {
13353                  $this->images[$image][$key] = $data;
13354              }
13355          }
13356  
13357          /**

13358          * Get page buffer content.

13359          * @param string $image image key

13360          * @return string image buffer content or false in case of error

13361          * @access protected

13362          * @since 4.5.000 (2008-12-31)

13363          */
13364  		protected function getImageBuffer($image) {
13365              if ($this->diskcache AND isset($this->images[$image])) {
13366                  return unserialize($this->readDiskCache($this->images[$image]));
13367              } elseif (isset($this->images[$image])) {
13368                  return $this->images[$image];
13369              }
13370              return false;
13371          }
13372  
13373          /**

13374          * Set font buffer content.

13375          * @param string $font font key

13376          * @param array $data font data

13377          * @access protected

13378          * @since 4.5.000 (2009-01-02)

13379          */
13380  		protected function setFontBuffer($font, $data) {
13381              if ($this->diskcache) {
13382                  if (!isset($this->fonts[$font])) {
13383                      $this->fonts[$font] = $this->getObjFilename('font');
13384                  }
13385                  $this->writeDiskCache($this->fonts[$font], serialize($data));
13386              } else {
13387                  $this->fonts[$font] = $data;
13388              }
13389              if (!in_array($font, $this->fontkeys)) {
13390                  $this->fontkeys[] = $font;
13391              }
13392          }
13393  
13394          /**

13395          * Set font buffer content.

13396          * @param string $font font key

13397          * @param string $key font sub-key

13398          * @param array $data font data

13399          * @access protected

13400          * @since 4.5.000 (2009-01-02)

13401          */
13402  		protected function setFontSubBuffer($font, $key, $data) {
13403              if (!isset($this->fonts[$font])) {
13404                  $this->setFontBuffer($font, array());
13405              }
13406              if ($this->diskcache) {
13407                  $tmpfont = $this->getFontBuffer($font);
13408                  $tmpfont[$key] = $data;
13409                  $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
13410              } else {
13411                  $this->fonts[$font][$key] = $data;
13412              }
13413          }
13414  
13415          /**

13416          * Get font buffer content.

13417          * @param string $font font key

13418          * @return string font buffer content or false in case of error

13419          * @access protected

13420          * @since 4.5.000 (2009-01-02)

13421          */
13422  		protected function getFontBuffer($font) {
13423              if ($this->diskcache AND isset($this->fonts[$font])) {
13424                  return unserialize($this->readDiskCache($this->fonts[$font]));
13425              } elseif (isset($this->fonts[$font])) {
13426                  return $this->fonts[$font];
13427              }
13428              return false;
13429          }
13430  
13431          /**

13432          * Move a page to a previous position.

13433          * @param int $frompage number of the source page

13434          * @param int $topage number of the destination page (must be less than $frompage)

13435          * @return true in case of success, false in case of error.

13436          * @access public

13437          * @since 4.5.000 (2009-01-02)

13438          */
13439  		public function movePage($frompage, $topage) {
13440              if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
13441                  return false;
13442              }
13443              if ($frompage == $this->page) {
13444                  // close the page before moving it

13445                  $this->endPage();
13446              }
13447              // move all page-related states

13448              $tmppage = $this->pages[$frompage];
13449              $tmppagedim = $this->pagedim[$frompage];
13450              $tmppagelen = $this->pagelen[$frompage];
13451              $tmpintmrk = $this->intmrk[$frompage];
13452              if (isset($this->footerpos[$frompage])) {
13453                  $tmpfooterpos = $this->footerpos[$frompage];
13454              }
13455              if (isset($this->footerlen[$frompage])) {
13456                  $tmpfooterlen = $this->footerlen[$frompage];
13457              }
13458              if (isset($this->transfmrk[$frompage])) {
13459                  $tmptransfmrk = $this->transfmrk[$frompage];
13460              }
13461              if (isset($this->PageAnnots[$frompage])) {
13462                  $tmpannots = $this->PageAnnots[$frompage];
13463              }
13464              if (isset($this->newpagegroup[$frompage])) {
13465                  $tmpnewpagegroup = $this->newpagegroup[$frompage];
13466              }
13467              for ($i = $frompage; $i > $topage; --$i) {
13468                  $j = $i - 1;
13469                  // shift pages down

13470                  $this->pages[$i] = $this->pages[$j];
13471                  $this->pagedim[$i] = $this->pagedim[$j];
13472                  $this->pagelen[$i] = $this->pagelen[$j];
13473                  $this->intmrk[$i] = $this->intmrk[$j];
13474                  if (isset($this->footerpos[$j])) {
13475                      $this->footerpos[$i] = $this->footerpos[$j];
13476                  } elseif (isset($this->footerpos[$i])) {
13477                      unset($this->footerpos[$i]);
13478                  }
13479                  if (isset($this->footerlen[$j])) {
13480                      $this->footerlen[$i] = $this->footerlen[$j];
13481                  } elseif (isset($this->footerlen[$i])) {
13482                      unset($this->footerlen[$i]);
13483                  }
13484                  if (isset($this->transfmrk[$j])) {
13485                      $this->transfmrk[$i] = $this->transfmrk[$j];
13486                  } elseif (isset($this->transfmrk[$i])) {
13487                      unset($this->transfmrk[$i]);
13488                  }
13489                  if (isset($this->PageAnnots[$j])) {
13490                      $this->PageAnnots[$i] = $this->PageAnnots[$j];
13491                  } elseif (isset($this->PageAnnots[$i])) {
13492                      unset($this->PageAnnots[$i]);
13493                  }
13494                  if (isset($this->newpagegroup[$j])) {
13495                      $this->newpagegroup[$i] = $this->newpagegroup[$j];
13496                  } elseif (isset($this->newpagegroup[$i])) {
13497                      unset($this->newpagegroup[$i]);
13498                  }
13499              }
13500              $this->pages[$topage] = $tmppage;
13501              $this->pagedim[$topage] = $tmppagedim;
13502              $this->pagelen[$topage] = $tmppagelen;
13503              $this->intmrk[$topage] = $tmpintmrk;
13504              if (isset($tmpfooterpos)) {
13505                  $this->footerpos[$topage] = $tmpfooterpos;
13506              } elseif (isset($this->footerpos[$topage])) {
13507                  unset($this->footerpos[$topage]);
13508              }
13509              if (isset($tmpfooterlen)) {
13510                  $this->footerlen[$topage] = $tmpfooterlen;
13511              } elseif (isset($this->footerlen[$topage])) {
13512                  unset($this->footerlen[$topage]);
13513              }
13514              if (isset($tmptransfmrk)) {
13515                  $this->transfmrk[$topage] = $tmptransfmrk;
13516              } elseif (isset($this->transfmrk[$topage])) {
13517                  unset($this->transfmrk[$topage]);
13518              }
13519              if (isset($tmpannots)) {
13520                  $this->PageAnnots[$topage] = $tmpannots;
13521              } elseif (isset($this->PageAnnots[$topage])) {
13522                  unset($this->PageAnnots[$topage]);
13523              }
13524              if (isset($tmpnewpagegroup)) {
13525                  $this->newpagegroup[$topage] = $tmpnewpagegroup;
13526              } elseif (isset($this->newpagegroup[$topage])) {
13527                  unset($this->newpagegroup[$topage]);
13528              }
13529              // adjust outlines

13530              $tmpoutlines = $this->outlines;
13531              foreach ($tmpoutlines as $key => $outline) {
13532                  if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
13533                      $this->outlines[$key]['p'] = $outline['p'] + 1;
13534                  } elseif ($outline['p'] == $frompage) {
13535                      $this->outlines[$key]['p'] = $topage;
13536                  }
13537              }
13538              // adjust links

13539              $tmplinks = $this->links;
13540              foreach ($tmplinks as $key => $link) {
13541                  if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
13542                      $this->links[$key][0] = $link[0] + 1;
13543                  } elseif ($link[0] == $frompage) {
13544                      $this->links[$key][0] = $topage;
13545                  }
13546              }
13547              // adjust javascript

13548              $tmpjavascript = $this->javascript;
13549              global $jfrompage, $jtopage;
13550              $jfrompage = $frompage;
13551              $jtopage = $topage;
13552              $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13553                  create_function('$matches', 'global $jfrompage, $jtopage;
13554                  $pagenum = intval($matches[3]) + 1;
13555                  if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
13556                      $newpage = ($pagenum + 1);
13557                  } elseif ($pagenum == $jfrompage) {
13558                      $newpage = $jtopage;
13559                  } else {
13560                      $newpage = $pagenum;
13561                  }
13562                  --$newpage;
13563                  return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13564              // return to last page

13565              $this->lastPage(true);
13566              return true;
13567          }
13568  
13569          /**

13570          * Remove the specified page.

13571          * @param int $page page to remove

13572          * @return true in case of success, false in case of error.

13573          * @access public

13574          * @since 4.6.004 (2009-04-23)

13575          */
13576  		public function deletePage($page) {
13577              if ($page > $this->numpages) {
13578                  return false;
13579              }
13580              // delete current page

13581              unset($this->pages[$page]);
13582              unset($this->pagedim[$page]);
13583              unset($this->pagelen[$page]);
13584              unset($this->intmrk[$page]);
13585              if (isset($this->footerpos[$page])) {
13586                  unset($this->footerpos[$page]);
13587              }
13588              if (isset($this->footerlen[$page])) {
13589                  unset($this->footerlen[$page]);
13590              }
13591              if (isset($this->transfmrk[$page])) {
13592                  unset($this->transfmrk[$page]);
13593              }
13594              if (isset($this->PageAnnots[$page])) {
13595                  unset($this->PageAnnots[$page]);
13596              }
13597              if (isset($this->newpagegroup[$page])) {
13598                  unset($this->newpagegroup[$page]);
13599              }
13600              if (isset($this->pageopen[$page])) {
13601                  unset($this->pageopen[$page]);
13602              }
13603              // update remaining pages

13604              for ($i = $page; $i < $this->numpages; ++$i) {
13605                  $j = $i + 1;
13606                  // shift pages

13607                  $this->pages[$i] = $this->pages[$j];
13608                  $this->pagedim[$i] = $this->pagedim[$j];
13609                  $this->pagelen[$i] = $this->pagelen[$j];
13610                  $this->intmrk[$i] = $this->intmrk[$j];
13611                  if (isset($this->footerpos[$j])) {
13612                      $this->footerpos[$i] = $this->footerpos[$j];
13613                  } elseif (isset($this->footerpos[$i])) {
13614                      unset($this->footerpos[$i]);
13615                  }
13616                  if (isset($this->footerlen[$j])) {
13617                      $this->footerlen[$i] = $this->footerlen[$j];
13618                  } elseif (isset($this->footerlen[$i])) {
13619                      unset($this->footerlen[$i]);
13620                  }
13621                  if (isset($this->transfmrk[$j])) {
13622                      $this->transfmrk[$i] = $this->transfmrk[$j];
13623                  } elseif (isset($this->transfmrk[$i])) {
13624                      unset($this->transfmrk[$i]);
13625                  }
13626                  if (isset($this->PageAnnots[$j])) {
13627                      $this->PageAnnots[$i] = $this->PageAnnots[$j];
13628                  } elseif (isset($this->PageAnnots[$i])) {
13629                      unset($this->PageAnnots[$i]);
13630                  }
13631                  if (isset($this->newpagegroup[$j])) {
13632                      $this->newpagegroup[$i] = $this->newpagegroup[$j];
13633                  } elseif (isset($this->newpagegroup[$i])) {
13634                      unset($this->newpagegroup[$i]);
13635                  }
13636                  if (isset($this->pageopen[$j])) {
13637                      $this->pageopen[$i] = $this->pageopen[$j];
13638                  } elseif (isset($this->pageopen[$i])) {
13639                      unset($this->pageopen[$i]);
13640                  }
13641              }
13642              // remove last page

13643              unset($this->pages[$this->numpages]);
13644              unset($this->pagedim[$this->numpages]);
13645              unset($this->pagelen[$this->numpages]);
13646              unset($this->intmrk[$this->numpages]);
13647              if (isset($this->footerpos[$this->numpages])) {
13648                  unset($this->footerpos[$this->numpages]);
13649              }
13650              if (isset($this->footerlen[$this->numpages])) {
13651                  unset($this->footerlen[$this->numpages]);
13652              }
13653              if (isset($this->transfmrk[$this->numpages])) {
13654                  unset($this->transfmrk[$this->numpages]);
13655              }
13656              if (isset($this->PageAnnots[$this->numpages])) {
13657                  unset($this->PageAnnots[$this->numpages]);
13658              }
13659              if (isset($this->newpagegroup[$this->numpages])) {
13660                  unset($this->newpagegroup[$this->numpages]);
13661              }
13662              if (isset($this->pageopen[$this->numpages])) {
13663                  unset($this->pageopen[$this->numpages]);
13664              }
13665              --$this->numpages;
13666              $this->page = $this->numpages;
13667              // adjust outlines

13668              $tmpoutlines = $this->outlines;
13669              foreach ($tmpoutlines as $key => $outline) {
13670                  if ($outline['p'] > $page) {
13671                      $this->outlines[$key]['p'] = $outline['p'] - 1;
13672                  } elseif ($outline['p'] == $page) {
13673                      unset($this->outlines[$key]);
13674                  }
13675              }
13676              // adjust links

13677              $tmplinks = $this->links;
13678              foreach ($tmplinks as $key => $link) {
13679                  if ($link[0] > $page) {
13680                      $this->links[$key][0] = $link[0] - 1;
13681                  } elseif ($link[0] == $page) {
13682                      unset($this->links[$key]);
13683                  }
13684              }
13685              // adjust javascript

13686              $tmpjavascript = $this->javascript;
13687              global $jpage;
13688              $jpage = $page;
13689              $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13690                  create_function('$matches', 'global $jpage;
13691                  $pagenum = intval($matches[3]) + 1;
13692                  if ($pagenum >= $jpage) {
13693                      $newpage = ($pagenum - 1);
13694                  } elseif ($pagenum == $jpage) {
13695                      $newpage = 1;
13696                  } else {
13697                      $newpage = $pagenum;
13698                  }
13699                  --$newpage;
13700                  return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13701              // return to last page

13702              $this->lastPage(true);
13703              return true;
13704          }
13705  
13706          /**

13707          * Output a Table of Content Index (TOC).

13708          * You can override this method to achieve different styles.

13709          * @param int $page page number where this TOC should be inserted (leave empty for current page).

13710          * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment).

13711          * @param string $filler string used to fill the space between text and page number.

13712          * @access public

13713          * @author Nicola Asuni

13714          * @since 4.5.000 (2009-01-02)

13715          */
13716  		public function addTOC($page='', $numbersfont='', $filler='.') {
13717              $fontsize = $this->FontSizePt;
13718              $fontfamily = $this->FontFamily;
13719              $fontstyle = $this->FontStyle;
13720              $w = $this->w - $this->lMargin - $this->rMargin;
13721              $spacer = $this->GetStringWidth(' ') * 4;
13722              $page_first = $this->getPage();
13723              $lmargin = $this->lMargin;
13724              $rmargin = $this->rMargin;
13725              $x_start = $this->GetX();
13726              if ($this->empty_string($numbersfont)) {
13727                  $numbersfont = $this->default_monospaced_font;
13728              }
13729              if ($this->empty_string($filler)) {
13730                  $filler = ' ';
13731              }
13732              if ($this->empty_string($page)) {
13733                  $gap = ' ';
13734              } else {
13735                  $gap = '';
13736              }
13737              foreach ($this->outlines as $key => $outline) {
13738                  if ($this->rtl) {
13739                      $aligntext = 'R';
13740                      $alignnum = 'L';
13741                  } else {
13742                      $aligntext = 'L';
13743                      $alignnum = 'R';
13744                  }
13745                  if ($outline['l'] == 0) {
13746                      $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
13747                  } else {
13748                      $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
13749                  }
13750                  $indent = ($spacer * $outline['l']);
13751                  if ($this->rtl) {
13752                      $this->rMargin += $indent;
13753                      $this->x -= $indent;
13754                  } else {
13755                      $this->lMargin += $indent;
13756                      $this->x += $indent;
13757                  }
13758                  $link = $this->AddLink();
13759                  $this->SetLink($link, 0, $outline['p']);
13760                  // write the text

13761                  $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
13762                  $this->SetFont($numbersfont, $fontstyle, $fontsize);
13763                  if ($this->empty_string($page)) {
13764                      $pagenum = $outline['p'];
13765                  } else {
13766                      // placemark to be replaced with the correct number

13767                      $pagenum = '{#'.($outline['p']).'}';
13768                      if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
13769                          $pagenum = '{'.$pagenum.'}';
13770                      }
13771                  }
13772                  $numwidth = $this->GetStringWidth($pagenum);
13773                  if ($this->rtl) {
13774                      $tw = $this->x - $this->lMargin;
13775                  } else {
13776                      $tw = $this->w - $this->rMargin - $this->x;
13777                  }
13778                  $fw = $tw - $numwidth - $this->GetStringWidth(' ');
13779                  $numfills = floor($fw / $this->GetStringWidth($filler));
13780                  if ($numfills > 0) {
13781                      $rowfill = str_repeat($filler, $numfills);
13782                  } else {
13783                      $rowfill = '';
13784                  }
13785                  if ($this->rtl) {
13786                      $pagenum = $pagenum.$gap.$rowfill.' ';
13787                  } else {
13788                      $pagenum = ' '.$rowfill.$gap.$pagenum;
13789                  }
13790                  // write the number

13791                  //$this->SetX($x_start);

13792                  $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
13793                  $this->SetX($x_start);
13794                  $this->lMargin = $lmargin;
13795                  $this->rMargin = $rmargin;
13796              }
13797              $page_last = $this->getPage();
13798              $numpages = $page_last - $page_first + 1;
13799              if (!$this->empty_string($page)) {
13800                  for ($p = $page_first; $p <= $page_last; ++$p) {
13801                      // get page data

13802                      $temppage = $this->getPageBuffer($p);
13803                      for ($n = 1; $n <= $this->numpages; ++$n) {
13804                          // update page numbers

13805                          $k = '{#'.$n.'}';
13806                          $ku = '{'.$k.'}';
13807                          $alias_a = $this->_escape($k);
13808                          $alias_au = $this->_escape('{'.$k.'}');
13809                          if ($this->isunicode) {
13810                              $alias_b = $this->_escape($this->UTF8ToLatin1($k));
13811                              $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
13812                              $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
13813                              $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
13814                          }
13815                          if ($n >= $page) {
13816                              $np = $n + $numpages;
13817                          } else {
13818                              $np = $n;
13819                          }
13820                          $ns = $this->formatTOCPageNumber($np);
13821                          $nu = $ns;
13822                          $sdiff = strlen($k) - strlen($ns) - 1;
13823                          $sdiffu = strlen($ku) - strlen($ns) - 1;
13824                          $sfill = str_repeat($filler, $sdiff);
13825                          $sfillu = str_repeat($filler, $sdiffu);
13826                          if ($this->rtl) {
13827                              $ns = $ns.' '.$sfill;
13828                              $nu = $nu.' '.$sfillu;
13829                          } else {
13830                              $ns = $sfill.' '.$ns;
13831                              $nu = $sfillu.' '.$nu;
13832                          }
13833                          $nu = $this->UTF8ToUTF16BE($nu, false);
13834                          $temppage = str_replace($alias_au, $nu, $temppage);
13835                          if ($this->isunicode) {
13836                              $temppage = str_replace($alias_bu, $nu, $temppage);
13837                              $temppage = str_replace($alias_cu, $nu, $temppage);
13838                              $temppage = str_replace($alias_b, $ns, $temppage);
13839                              $temppage = str_replace($alias_c, $ns, $temppage);
13840                          }
13841                          $temppage = str_replace($alias_a, $ns, $temppage);
13842                      }
13843                      // save changes

13844                      $this->setPageBuffer($p, $temppage);
13845                  }
13846                  // move pages

13847                  for ($i = 0; $i < $numpages; ++$i) {
13848                      $this->movePage($page_last, $page);
13849                  }
13850              }
13851              $this->SetFont($fontfamily, $fontstyle, $fontsize);
13852          }
13853  
13854          /**

13855          * Stores a copy of the current TCPDF object used for undo operation.

13856          * @access public

13857          * @since 4.5.029 (2009-03-19)

13858          */
13859  		public function startTransaction() {
13860              if (isset($this->objcopy)) {
13861                  // remove previous copy

13862                  $this->commitTransaction();
13863              }
13864              // clone current object

13865              $this->objcopy = $this->objclone($this);
13866          }
13867  
13868          /**

13869          * Delete the copy of the current TCPDF object used for undo operation.

13870          * @access public

13871          * @since 4.5.029 (2009-03-19)

13872          */
13873  		public function commitTransaction() {
13874              if (isset($this->objcopy)) {
13875                  $this->objcopy->_destroy(true, true);
13876                  unset($this->objcopy);
13877              }
13878          }
13879  
13880          /**

13881          * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().

13882          * @return TCPDF object.

13883          * @access public

13884          * @since 4.5.029 (2009-03-19)

13885          */
13886  		public function rollbackTransaction() {
13887              if (isset($this->objcopy)) {
13888                  if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
13889                      // truncate files to previous values

13890                      foreach ($this->objcopy->cache_file_lenght as $file => $lenght) {
13891                          $file = substr($file, 1);
13892                          $handle = fopen($file, 'r+');
13893                          ftruncate($handle, $lenght);
13894                      }
13895                  }
13896                  $this->_destroy(true, true);
13897                  return $this->objcopy;
13898              }
13899              return $this;
13900          }
13901  
13902          /**

13903          * Creates a copy of a class object

13904          * @param object $object class object to be cloned

13905          * @return cloned object

13906          * @access public

13907          * @since 4.5.029 (2009-03-19)

13908          */
13909  		public function objclone($object) {
13910              return @clone($object);
13911          }
13912  
13913          /**

13914          * Determine whether a string is empty.

13915          * @param srting $str string to be checked

13916          * @return boolean true if string is empty

13917          * @access public

13918          * @since 4.5.044 (2009-04-16)

13919          */
13920  		public function empty_string($str) {
13921              return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
13922          }
13923          //////////////////////////////////// CRM extensions ////////////////////////////////////////////////////////////////////////////////////

13924          /**

13925          * formats a given text

13926          * @param srting $maxwidth to which the text is formated, $text to be formated

13927          * @return count

13928          * @access public

13929          */
13930  		function WordWrapText(&$text, $maxwidth)
13931          {
13932              $text = trim($text);
13933              if ($text==='')
13934                  return 0;
13935              $space = $this->GetStringWidth(' ');
13936              $lines = explode("\n", $text);
13937              $text = '';
13938              $count = 0;
13939  
13940              foreach ($lines as $line)
13941              {
13942                  $words = preg_split('/ +/', $line);
13943                  $width = 0;
13944  
13945                  foreach ($words as $word)
13946                  {
13947                  //special condition for empty lines

13948                  if ((bin2hex($word) == '0d') OR ($word == '')) $word = "&nbsp;";
13949                  $wordwidth = $this->GetStringWidth($word);
13950                  if ($width + $wordwidth <= $maxwidth)
13951                      {
13952                          $width += $wordwidth + $space;
13953                          $text .= $word.' ';
13954                      }
13955                      else
13956                      {
13957                          $width = $wordwidth + $space;
13958                          $text = rtrim($text)."\n".$word.' ';
13959                          $count++;
13960                      }
13961                  }
13962                  $text = rtrim($text)."\n";
13963                  $count++;
13964              }
13965              $text = rtrim($text);
13966              return $count;
13967          }
13968          /**

13969          * checks whether a new page must be added

13970          * @param srting $y_location current location

13971          * @return true/false

13972          * @access public

13973          */
13974  		function CheckPageBreakPDF($y_location)
13975          {
13976              //If the next line would cause an overflow, add a new page immediately

13977              if($y_location >$this->PageBreakTrigger)
13978              {
13979                  $this->AddPage($this->CurOrientation);
13980                  return (true);
13981              }
13982              else return (false);
13983          }
13984          /**

13985          * checks whether a new page must be added to have enough space for the summary

13986          * @param srting $h hight of the summary

13987          * @return true/false

13988          * @access public

13989          */
13990  		function CheckPageBreakSummary($h)
13991          {
13992              //If the height h would cause an overflow, add a new page immediately

13993              if($this->GetY()+$h>($this->PageBreakTrigger))
13994              {
13995                  $this->AddPage($this->CurOrientation);
13996                  return (true);
13997              }
13998              else return (false);
13999          }
14000          
14001      } // END OF TCPDF CLASS

14002  }
14003  //============================================================+

14004  // END OF FILE

14005  //============================================================+

14006  ?>


Generated: Fri Nov 28 20:08:37 2014 Cross-referenced by PHPXref 0.7.1