[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

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

   1  <?php
   2  //============================================================+
   3  // File name   : tcpdf.php
   4  // Begin       : 2002-08-03
   5  // Last Update : 2008-03-07
   6  // Author      : Nicola Asuni
   7  // Version     : 2.5.000_PHP4
   8  // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
   9  //
  10  // Description : This is a PHP5 class for generating PDF files
  11  //               on-the-fly without requiring external
  12  //               extensions.
  13  //
  14  // NOTE:
  15  // This class was originally derived in 2002 from the Public
  16  // Domain FPDF class by Olivier Plathey (http://www.fpdf.org).
  17  //
  18  // Main features:
  19  //  - supports all ISO page formats;
  20  //  - supports UTF-8 Unicode and Right-To-Left languages;
  21  //  - supports document encryption;
  22  //  - includes methods to publish some xhtml code;
  23  //  - includes graphic and transformation methods;
  24  //  - includes bookmarks;
  25  //  - includes Javascript and forms support;
  26  //  - includes a method to print various barcode formats using an improved version of "Generic Barcode Render Class" by Karim Mribti (http://www.mribti.com/barcode/) (require GD library: http://www.boutell.com/gd/)
  27  //  - supports TrueTypeUnicode, TrueType, Type1 and encoding;
  28  //  - supports custom page formats, margins and units of measure;
  29  //  - includes methods for page header and footer management;
  30  //  - supports automatic page break;
  31  //  - supports automatic page numbering;
  32  //  - supports automatic line break and text justification;
  33  //  - supports JPEG, PNG anf GIF images;
  34  //  - supports colors;
  35  //  - supports links;
  36  //  - support page compression (require zlib extension: http://www.gzip.org/zlib/);
  37  //  - the source code is full documented in PhpDocumentor Style (http://www.phpdoc.org).
  38  //
  39  // -----------------------------------------------------------
  40  // THANKS TO:
  41  //
  42  // Olivier Plathey (http://www.fpdf.org) for original FPDF.
  43  // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
  44  // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
  45  // Warren Sherliker (wsherliker@gmail.com) for better image handling.
  46  // dullus for text Justification.
  47  // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
  48  // Patrick Benny for text stretch suggestion on Cell().
  49  // Johannes G�ntert for JavaScript support.
  50  // Denis Van Nuffelen for Dynamic Form.
  51  // Jacek Czekaj for multibyte justification
  52  // Anthony Ferrara for the reintroduction of legacy image methods.
  53  // Anyone that has reported a bug or sent a suggestion.
  54  //============================================================+
  55  
  56  
  57  
  58  /**
  59   * This is a PHP5 class for generating PDF files on-the-fly without requiring external extensions.<br>
  60   * TCPDF project (http://tcpdf.sourceforge.net) has been originally derived from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).<br>
  61   * <h3>TCPDF main features are:</h3>
  62   * <ul>
  63   * <li>supports all ISO page formats;</li>
  64   * <li>supports UTF-8 Unicode and Right-To-Left languages;</li>
  65   * <li>supports document encryption;</li>
  66   * <li>includes methods to publish some xhtml code, supporting the following elements: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small;</li>
  67   * <li>includes a method to print various barcode formats using an improved version of "Generic Barcode Render Class" by Karim Mribti (<a href="http://www.mribti.com/barcode/" target="_blank" title="Generic Barcode Render Class by Karim Mribti">http://www.mribti.com/barcode/</a>) (require GD library: <a href="http://www.boutell.com/gd/" target="_blank" title="GD library">http://www.boutell.com/gd/</a>)</li>
  68   * <li>supports TrueTypeUnicode, TrueType, Type1 and encoding; </li>
  69   * <li>supports custom page formats, margins and units of measure;</li>
  70   * <li>includes methods for page header and footer management;</li>
  71   * <li>supports automatic page break;</li>
  72   * <li>supports automatic page numbering;</li>
  73   * <li>supports automatic line break and text justification;</li>
  74   * <li>supports JPEG, PNG anf GIF images;</li>
  75   * <li>supports colors;</li>
  76   * <li>supports links;</li>
  77   * <li>support page compression (require zlib extension: <a href="http://www.gzip.org/zlib/" target="_blank" title="zlib">http://www.gzip.org/zlib/</a>);</li>
  78   * <li>the source code is full documented in PhpDocumentor Style (<a href="http://www.phpdoc.org" target="_blank" title="phpDocumentor">http://www.phpdoc.org</a>).</li>
  79   * </ul>
  80   * Tools to encode your unicode fonts are on fonts/ttf2ufm directory.</p>
  81   * @name TCPDF
  82   * @package com.tecnick.tcpdf
  83   * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
  84   * @author Nicola Asuni
  85   * @copyright 2004-2008 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
  86   * @link http://www.tcpdf.org
  87   * @license http://www.gnu.org/copyleft/lesser.html LGPL
  88   * @version 2.5.000_PHP4
  89   */
  90  
  91  /**
  92   * include configuration file
  93   */
  94  require_once(dirname(__FILE__).'/config/tcpdf_config.php');
  95  
  96  if(!class_exists('TCPDF')) {
  97      /**
  98       * define default PDF document producer
  99       */
 100      define('PDF_PRODUCER','TCPDF 2.5.000_PHP4 (http://www.tcpdf.org)');
 101  
 102      /**
 103      * This is a PHP5 class for generating PDF files on-the-fly without requiring external extensions.<br>
 104      * TCPDF project (http://tcpdf.sourceforge.net) has been originally derived from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).<br>
 105      * To add your own TTF fonts please read /fonts/README.TXT
 106      * @name TCPDF
 107      * @package com.tecnick.tcpdf
 108      * @version 2.5.000_PHP4
 109      * @author Nicola Asuni
 110      * @link http://www.tcpdf.org
 111      * @license http://www.gnu.org/copyleft/lesser.html LGPL
 112      */
 113      class TCPDF {
 114  
 115          // Private or Protected properties
 116  
 117          /**
 118          * @var current page number
 119          * @access protected
 120          */
 121          var $page;
 122  
 123          /**
 124          * @var current object number
 125          * @access protected
 126          */
 127          var $n;
 128  
 129          /**
 130          * @var array of object offsets
 131          * @access protected
 132          */
 133          var $offsets;
 134  
 135          /**
 136          * @var buffer holding in-memory PDF
 137          * @access protected
 138          */
 139          var $buffer;
 140  
 141          /**
 142          * @var array containing pages
 143          * @access protected
 144          */
 145          var $pages;
 146  
 147          /**
 148          * @var current document state
 149          * @access protected
 150          */
 151          var $state;
 152  
 153          /**
 154          * @var compression flag
 155          * @access protected
 156          */
 157          var $compress;
 158  
 159          /**
 160          * @var default page orientation (P = Portrait, L = Landscape)
 161          * @access protected
 162          */
 163          var $DefOrientation;
 164  
 165          /**
 166          * @var current page orientation (P = Portrait, L = Landscape)
 167          * @access protected
 168          */
 169          var $CurOrientation;
 170  
 171          /**
 172          * @var array indicating page orientation changes
 173          * @access protected
 174          */
 175          var $OrientationChanges;
 176  
 177          /**
 178          * @var scale factor (number of points in user unit)
 179          * @access protected
 180          */
 181          var $k;
 182  
 183          /**
 184          * @var width of page format in points
 185          * @access protected
 186          */
 187          var $fwPt;
 188  
 189          /**
 190          * @var height of page format in points
 191          * @access protected
 192          */
 193          var $fhPt;
 194  
 195          /**
 196          * @var width of page format in user unit
 197          * @access protected
 198          */
 199          var $fw;
 200  
 201          /**
 202          * @var height of page format in user unit
 203          * @access protected
 204          */
 205          var $fh;
 206  
 207          /**
 208          * @var current width of page in points
 209          * @access protected
 210          */
 211          var $wPt;
 212  
 213          /**
 214          * @var current height of page in points
 215          * @access protected
 216          */
 217          var $hPt;
 218  
 219          /**
 220          * @var current width of page in user unit
 221          * @access protected
 222          */
 223          var $w;
 224  
 225          /**
 226          * @var current height of page in user unit
 227          * @access protected
 228          */
 229          var $h;
 230  
 231          /**
 232          * @var left margin
 233          * @access protected
 234          */
 235          var $lMargin;
 236  
 237          /**
 238          * @var top margin
 239          * @access protected
 240          */
 241          var $tMargin;
 242  
 243          /**
 244          * @var right margin
 245          * @access protected
 246          */
 247          var $rMargin;
 248  
 249          /**
 250          * @var page break margin
 251          * @access protected
 252          */
 253          var $bMargin;
 254  
 255          /**
 256          * @var cell internal padding
 257          * @access protected
 258          */
 259          var $cMargin;
 260  
 261          /**
 262          * @var current horizontal position in user unit for cell positioning
 263          * @access protected
 264          */
 265          var $x;
 266  
 267          /**
 268          * @var current vertical position in user unit for cell positioning
 269          * @access protected
 270          */
 271          var $y;
 272  
 273          /**
 274          * @var height of last cell printed
 275          * @access protected
 276          */
 277          var $lasth;
 278  
 279          /**
 280          * @var line width in user unit
 281          * @access protected
 282          */
 283          var $LineWidth;
 284  
 285          /**
 286          * @var array of standard font names
 287          * @access protected
 288          */
 289          var $CoreFonts;
 290  
 291          /**
 292          * @var array of used fonts
 293          * @access protected
 294          */
 295          var $fonts;
 296  
 297          /**
 298          * @var array of font files
 299          * @access protected
 300          */
 301          var $FontFiles;
 302  
 303          /**
 304          * @var array of encoding differences
 305          * @access protected
 306          */
 307          var $diffs;
 308  
 309          /**
 310          * @var array of used images
 311          * @access protected
 312          */
 313          var $images;
 314  
 315          /**
 316          * @var array of links in pages
 317          * @access protected
 318          */
 319          var $PageLinks;
 320  
 321          /**
 322          * @var array of internal links
 323          * @access protected
 324          */
 325          var $links;
 326  
 327          /**
 328          * @var current font family
 329          * @access protected
 330          */
 331          var $FontFamily;
 332  
 333          /**
 334          * @var current font style
 335          * @access protected
 336          */
 337          var $FontStyle;
 338  
 339          /**
 340          * @var underlining flag
 341          * @access protected
 342          */
 343          var $underline;
 344  
 345          /**
 346          * @var current font info
 347          * @access protected
 348          */
 349          var $CurrentFont;
 350  
 351          /**
 352          * @var current font size in points
 353          * @access protected
 354          */
 355          var $FontSizePt;
 356  
 357          /**
 358          * @var current font size in user unit
 359          * @access protected
 360          */
 361          var $FontSize;
 362  
 363          /**
 364          * @var commands for drawing color
 365          * @access protected
 366          */
 367          var $DrawColor;
 368  
 369          /**
 370          * @var commands for filling color
 371          * @access protected
 372          */
 373          var $FillColor;
 374  
 375          /**
 376          * @var commands for text color
 377          * @access protected
 378          */
 379          var $TextColor;
 380  
 381          /**
 382          * @var indicates whether fill and text colors are different
 383          * @access protected
 384          */
 385          var $ColorFlag;
 386  
 387          /**
 388          * @var word spacing
 389          * @access protected
 390          */
 391          var $ws;
 392  
 393          /**
 394          * @var automatic page breaking
 395          * @access protected
 396          */
 397          var $AutoPageBreak;
 398  
 399          /**
 400          * @var threshold used to trigger page breaks
 401          * @access protected
 402          */
 403          var $PageBreakTrigger;
 404  
 405          /**
 406          * @var flag set when processing footer
 407          * @access protected
 408          */
 409          var $InFooter;
 410  
 411          /**
 412          * @var zoom display mode
 413          * @access protected
 414          */
 415          var $ZoomMode;
 416  
 417          /**
 418          * @var layout display mode
 419          * @access protected
 420          */
 421          var $LayoutMode;
 422  
 423          /**
 424          * @var title
 425          * @access protected
 426          */
 427          var $title;
 428  
 429          /**
 430          * @var subject
 431          * @access protected
 432          */
 433          var $subject;
 434  
 435          /**
 436          * @var author
 437          * @access protected
 438          */
 439          var $author;
 440  
 441          /**
 442          * @var keywords
 443          * @access protected
 444          */
 445          var $keywords;
 446  
 447          /**
 448          * @var creator
 449          * @access protected
 450          */
 451          var $creator;
 452  
 453          /**
 454          * @var alias for total number of pages
 455          * @access protected
 456          */
 457          var $AliasNbPages;
 458  
 459          /**
 460          * @var right-bottom corner X coordinate of inserted image
 461          * @since 2002-07-31
 462          * @author Nicola Asuni
 463          * @access protected
 464          */
 465          var $img_rb_x;
 466  
 467          /**
 468          * @var right-bottom corner Y coordinate of inserted image
 469          * @since 2002-07-31
 470          * @author Nicola Asuni
 471          * @access protected
 472          */
 473          var $img_rb_y;
 474  
 475          /**
 476          * @var image scale factor
 477          * @since 2004-06-14
 478          * @author Nicola Asuni
 479          * @access protected
 480          */
 481          var $imgscale = 1;
 482  
 483          /**
 484          * @var boolean set to true when the input text is unicode (require unicode fonts)
 485          * @since 2005-01-02
 486          * @author Nicola Asuni
 487          * @access protected
 488          */
 489          var $isunicode = false;
 490  
 491          /**
 492          * @var PDF version
 493          * @since 1.5.3
 494          * @access protected
 495          */
 496          var $PDFVersion = "1.5";
 497  
 498  
 499          // ----------------------
 500  
 501          /**
 502           * @var Minimum distance between header and top page margin.
 503           * @access private
 504           */
 505          var $header_margin;
 506  
 507          /**
 508           * @var Minimum distance between footer and bottom page margin.
 509           * @access private
 510           */
 511          var $footer_margin;
 512  
 513          /**
 514           * @var original left margin value
 515           * @access private
 516           * @since 1.53.0.TC013
 517           */
 518          var $original_lMargin;
 519  
 520          /**
 521           * @var original right margin value
 522           * @access private
 523           * @since 1.53.0.TC013
 524           */
 525          var $original_rMargin;
 526  
 527          /**
 528           * @var Header font.
 529           * @access private
 530           */
 531          var $header_font;
 532  
 533          /**
 534           * @var Footer font.
 535           * @access private
 536           */
 537          var $footer_font;
 538  
 539          /**
 540           * @var Language templates.
 541           * @access private
 542           */
 543          var $l;
 544  
 545          /**
 546           * @var Barcode to print on page footer (only if set).
 547           * @access private
 548           */
 549          var $barcode = false;
 550  
 551          /**
 552           * @var If true prints header
 553           * @access private
 554           */
 555          var $print_header = true;
 556  
 557          /**
 558           * @var If true prints footer.
 559           * @access private
 560           */
 561          var $print_footer = true;
 562  
 563          /**
 564           * @var Header width (0 = full page width).
 565           * @access private
 566           */
 567          var $header_width = 0;
 568  
 569          /**
 570           * @var Header image logo.
 571           * @access private
 572           */
 573          var $header_logo = "";
 574  
 575          /**
 576           * @var Header image logo width in mm.
 577           * @access private
 578           */
 579          var $header_logo_width = 30;
 580  
 581          /**
 582           * @var String to print as title on document header.
 583           * @access private
 584           */
 585          var $header_title = "";
 586  
 587          /**
 588           * @var String to print on document header.
 589           * @access private
 590           */
 591          var $header_string = "";
 592  
 593          /**
 594           * @var Default number of columns for html table.
 595           * @access private
 596           */
 597          var $default_table_columns = 4;
 598  
 599  
 600          // variables for html parser
 601  
 602          /**
 603           * @var HTML PARSER: store current link.
 604           * @access private
 605           */
 606          var $HREF;
 607  
 608          /**
 609           * @var HTML PARSER: store font list.
 610           * @access private
 611           */
 612          var $fontList;
 613  
 614          /**
 615           * @var HTML PARSER: true when font attribute is set.
 616           * @access private
 617           */
 618          var $issetfont;
 619  
 620          /**
 621           * @var HTML PARSER: true when color attribute is set.
 622           * @access private
 623           */
 624          var $issetcolor;
 625  
 626          /**
 627           * @var HTML PARSER: true in case of ordered list (OL), false otherwise.
 628           * @access private
 629           */
 630          var $listordered = false;
 631  
 632          /**
 633           * @var HTML PARSER: count list items.
 634           * @access private
 635           */
 636          var $listcount = 0;
 637  
 638          /**
 639           * @var HTML PARSER: size of table border.
 640           * @access private
 641           */
 642          var $tableborder = 0;
 643  
 644          /**
 645           * @var HTML PARSER: true at the beginning of table.
 646           * @access private
 647           */
 648          var $tdbegin = false;
 649  
 650          /**
 651           * @var HTML PARSER: table width.
 652           * @access private
 653           */
 654          var $tdwidth = 0;
 655  
 656          /**
 657           * @var HTML PARSER: table height.
 658           * @access private
 659           */
 660          var $tdheight = 0;
 661  
 662          /**
 663           * @var HTML PARSER: table align.
 664           * @access private
 665           */
 666          var $tdalign = "L";
 667  
 668          /**
 669           * @var HTML PARSER: table background color.
 670           * @access private
 671           */
 672          var $tdbgcolor = false;
 673  
 674          /**
 675           * @var Store temporary font size in points.
 676           * @access private
 677           */
 678          var $tempfontsize = 10;
 679  
 680          /**
 681           * @var Bold font style status.
 682           * @access private
 683           */
 684          var $b;
 685  
 686          /**
 687           * @var Underlined font style status.
 688           * @access private
 689           */
 690          var $u;
 691  
 692          /**
 693           * @var Italic font style status.
 694           * @access private
 695           */
 696          var $i;
 697  
 698          /**
 699           * @var spacer for LI tags.
 700           * @access private
 701           */
 702          var $lispacer = "";
 703  
 704          /**
 705           * @var default encoding
 706           * @access private
 707           * @since 1.53.0.TC010
 708           */
 709          var $encoding = "UTF-8";
 710  
 711          /**
 712           * @var PHP internal encoding
 713           * @access private
 714           * @since 1.53.0.TC016
 715           */
 716          var $internal_encoding;
 717  
 718          /**
 719           * @var store previous fill color as RGB array
 720           * @access private
 721           * @since 1.53.0.TC017
 722           */
 723          var $prevFillColor = array(255,255,255);
 724  
 725          /**
 726           * @var store previous text color as RGB array
 727           * @access private
 728           * @since 1.53.0.TC017
 729           */
 730          var $prevTextColor = array(0,0,0);
 731  
 732          /**
 733           * @var store previous font family
 734           * @access private
 735           * @since 1.53.0.TC017
 736           */
 737          var $prevFontFamily;
 738  
 739          /**
 740           * @var store previous font style
 741           * @access private
 742           * @since 1.53.0.TC017
 743           */
 744          var $prevFontStyle;
 745  
 746          /**
 747           * @var indicates if the document language is Right-To-Left
 748           * @access private
 749           * @since 2.0.000
 750           */
 751          var $rtl = false;
 752  
 753          /**
 754           * @var used to force RTL or LTR string inversion
 755           * @access private
 756           * @since 2.0.000
 757           */
 758          var $tmprtl = false;
 759  
 760          // --- Variables used for document encryption:
 761  
 762          /**
 763           * Indicates whether document is protected
 764           * @access private
 765           * @since 2.0.000 (2008-01-02)
 766           */
 767          var $encrypted;
 768  
 769          /**
 770           * U entry in pdf document
 771           * @access private
 772           * @since 2.0.000 (2008-01-02)
 773           */
 774          var $Uvalue;
 775  
 776          /**
 777           * O entry in pdf document
 778           * @access private
 779           * @since 2.0.000 (2008-01-02)
 780           */
 781          var $Ovalue;
 782  
 783          /**
 784           * P entry in pdf document
 785           * @access private
 786           * @since 2.0.000 (2008-01-02)
 787           */
 788          var $Pvalue;
 789  
 790          /**
 791           * encryption object id
 792           * @access private
 793           * @since 2.0.000 (2008-01-02)
 794           */
 795          var $enc_obj_id;
 796  
 797          /**
 798           * last RC4 key encrypted (cached for optimisation)
 799           * @access private
 800           * @since 2.0.000 (2008-01-02)
 801           */
 802          var $last_rc4_key;
 803  
 804          /**
 805           * last RC4 computed key
 806           * @access private
 807           * @since 2.0.000 (2008-01-02)
 808           */
 809          var $last_rc4_key_c;
 810  
 811          // --- bookmark ---
 812  
 813          /**
 814           * Outlines for bookmark
 815           * @access private
 816           * @since 2.1.002 (2008-02-12)
 817           */
 818          var $outlines = array();
 819  
 820          /**
 821           * Outline root for bookmark
 822           * @access private
 823           * @since 2.1.002 (2008-02-12)
 824           */
 825          var $OutlineRoot;
 826  
 827  
 828          // --- javascript and form ---
 829  
 830          /**
 831           * javascript code
 832           * @access private
 833           * @since 2.1.002 (2008-02-12)
 834           */
 835          var $javascript = "";
 836  
 837          /**
 838           * javascript counter
 839           * @access private
 840           * @since 2.1.002 (2008-02-12)
 841           */
 842      var $n_js;
 843  
 844          //------------------------------------------------------------
 845          // Public methods
 846          //------------------------------------------------------------
 847  
 848          /**
 849           * This is the class constructor.
 850           * It allows to set up the page format, the orientation and
 851           * the measure unit used in all the methods (except for the font sizes).
 852           * @since 1.0
 853           * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
 854           * @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.
 855           * @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>
 856           * @param boolean $unicode TRUE means that the input text is unicode (default = true)
 857           * @param String $encoding charset encoding; default is UTF-8
 858           */
 859  		function TCPDF($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding="UTF-8") {
 860  
 861              /* Set internal character encoding to ASCII */
 862              if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) {
 863                  $this->internal_encoding = mb_internal_encoding();
 864                  mb_internal_encoding("ASCII");
 865              }
 866  
 867              // set language direction
 868              $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
 869              $this->tmprtl = false;
 870  
 871              //Some checks
 872              $this->_dochecks();
 873  
 874              //Initialization of properties
 875              $this->isunicode=$unicode;
 876              $this->page=0;
 877              $this->n=2;
 878              $this->buffer='';
 879              $this->pages=array();
 880              $this->OrientationChanges=array();
 881              $this->state=0;
 882              $this->fonts=array();
 883              $this->FontFiles=array();
 884              $this->diffs=array();
 885              $this->images=array();
 886              $this->links=array();
 887              $this->InFooter=false;
 888              $this->lasth=0;
 889              $this->FontFamily='';
 890              $this->FontStyle='';
 891              $this->FontSizePt=12;
 892              $this->underline=false;
 893              $this->DrawColor='0 G';
 894              $this->FillColor='0 g';
 895              $this->TextColor='0 g';
 896              $this->ColorFlag=false;
 897              $this->ws=0;
 898              // encryption values
 899              $this->encrypted=false;
 900              $this->last_rc4_key='';
 901              $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";
 902  
 903              //Standard Unicode fonts
 904              $this->CoreFonts=array(
 905              'courier'=>'Courier',
 906              'courierB'=>'Courier-Bold',
 907              'courierI'=>'Courier-Oblique',
 908              'courierBI'=>'Courier-BoldOblique',
 909              'helvetica'=>'Helvetica',
 910              'helveticaB'=>'Helvetica-Bold',
 911              'helveticaI'=>'Helvetica-Oblique',
 912              'helveticaBI'=>'Helvetica-BoldOblique',
 913              'times'=>'Times-Roman',
 914              'timesB'=>'Times-Bold',
 915              'timesI'=>'Times-Italic',
 916              'timesBI'=>'Times-BoldItalic',
 917              'symbol'=>'Symbol',
 918              'zapfdingbats'=>'ZapfDingbats'
 919              );
 920  
 921              //Scale factor
 922              switch (strtolower($unit)){
 923                  case 'pt': {$this->k=1; break;}
 924                  case 'mm': {$this->k=72/25.4; break;}
 925                  case 'cm': {$this->k=72/2.54; break;}
 926                  case 'in': {$this->k=72; break;}
 927                  default : {$this->Error('Incorrect unit: '.$unit); break;}
 928              }
 929  
 930              //Page format
 931              if(is_string($format)) {
 932                  // Page formats (45 standard ISO paper formats and 4 american common formats).
 933                  // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
 934                  switch (strtoupper($format)){
 935                      case '4A0': {$format = array(4767.87,6740.79); break;}
 936                      case '2A0': {$format = array(3370.39,4767.87); break;}
 937                      case 'A0': {$format = array(2383.94,3370.39); break;}
 938                      case 'A1': {$format = array(1683.78,2383.94); break;}
 939                      case 'A2': {$format = array(1190.55,1683.78); break;}
 940                      case 'A3': {$format = array(841.89,1190.55); break;}
 941                      case 'A4': default: {$format = array(595.28,841.89); break;}
 942                      case 'A5': {$format = array(419.53,595.28); break;}
 943                      case 'A6': {$format = array(297.64,419.53); break;}
 944                      case 'A7': {$format = array(209.76,297.64); break;}
 945                      case 'A8': {$format = array(147.40,209.76); break;}
 946                      case 'A9': {$format = array(104.88,147.40); break;}
 947                      case 'A10': {$format = array(73.70,104.88); break;}
 948                      case 'B0': {$format = array(2834.65,4008.19); break;}
 949                      case 'B1': {$format = array(2004.09,2834.65); break;}
 950                      case 'B2': {$format = array(1417.32,2004.09); break;}
 951                      case 'B3': {$format = array(1000.63,1417.32); break;}
 952                      case 'B4': {$format = array(708.66,1000.63); break;}
 953                      case 'B5': {$format = array(498.90,708.66); break;}
 954                      case 'B6': {$format = array(354.33,498.90); break;}
 955                      case 'B7': {$format = array(249.45,354.33); break;}
 956                      case 'B8': {$format = array(175.75,249.45); break;}
 957                      case 'B9': {$format = array(124.72,175.75); break;}
 958                      case 'B10': {$format = array(87.87,124.72); break;}
 959                      case 'C0': {$format = array(2599.37,3676.54); break;}
 960                      case 'C1': {$format = array(1836.85,2599.37); break;}
 961                      case 'C2': {$format = array(1298.27,1836.85); break;}
 962                      case 'C3': {$format = array(918.43,1298.27); break;}
 963                      case 'C4': {$format = array(649.13,918.43); break;}
 964                      case 'C5': {$format = array(459.21,649.13); break;}
 965                      case 'C6': {$format = array(323.15,459.21); break;}
 966                      case 'C7': {$format = array(229.61,323.15); break;}
 967                      case 'C8': {$format = array(161.57,229.61); break;}
 968                      case 'C9': {$format = array(113.39,161.57); break;}
 969                      case 'C10': {$format = array(79.37,113.39); break;}
 970                      case 'RA0': {$format = array(2437.80,3458.27); break;}
 971                      case 'RA1': {$format = array(1729.13,2437.80); break;}
 972                      case 'RA2': {$format = array(1218.90,1729.13); break;}
 973                      case 'RA3': {$format = array(864.57,1218.90); break;}
 974                      case 'RA4': {$format = array(609.45,864.57); break;}
 975                      case 'SRA0': {$format = array(2551.18,3628.35); break;}
 976                      case 'SRA1': {$format = array(1814.17,2551.18); break;}
 977                      case 'SRA2': {$format = array(1275.59,1814.17); break;}
 978                      case 'SRA3': {$format = array(907.09,1275.59); break;}
 979                      case 'SRA4': {$format = array(637.80,907.09); break;}
 980                      case 'LETTER': {$format = array(612.00,792.00); break;}
 981                      case 'LEGAL': {$format = array(612.00,1008.00); break;}
 982                      case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
 983                      case 'FOLIO': {$format = array(612.00,936.00); break;}
 984                      // default: {$this->Error('Unknown page format: '.$format); break;}
 985                  }
 986                  $this->fwPt=$format[0];
 987                  $this->fhPt=$format[1];
 988              }
 989              else {
 990                  $this->fwPt=$format[0]*$this->k;
 991                  $this->fhPt=$format[1]*$this->k;
 992              }
 993  
 994              $this->fw=$this->fwPt/$this->k;
 995              $this->fh=$this->fhPt/$this->k;
 996  
 997              //Page orientation
 998              $orientation=strtolower($orientation);
 999              if($orientation=='p' or $orientation=='portrait') {
1000                  $this->DefOrientation='P';
1001                  $this->wPt=$this->fwPt;
1002                  $this->hPt=$this->fhPt;
1003              }
1004              elseif($orientation=='l' or $orientation=='landscape') {
1005                  $this->DefOrientation='L';
1006                  $this->wPt=$this->fhPt;
1007                  $this->hPt=$this->fwPt;
1008              }
1009              else {
1010                  $this->Error('Incorrect orientation: '.$orientation);
1011              }
1012  
1013              $this->CurOrientation=$this->DefOrientation;
1014              $this->w=$this->wPt/$this->k;
1015              $this->h=$this->hPt/$this->k;
1016              //Page margins (1 cm)
1017              $margin=28.35/$this->k;
1018              $this->SetMargins($margin,$margin);
1019              //Interior cell margin (1 mm)
1020              $this->cMargin=$margin/10;
1021              //Line width (0.2 mm)
1022              $this->LineWidth=.567/$this->k;
1023              //Automatic page break
1024              $this->SetAutoPageBreak(true,2*$margin);
1025              //Full width display mode
1026              $this->SetDisplayMode('fullwidth');
1027              //Compression
1028              $this->SetCompression(true);
1029              //Set default PDF version number
1030              $this->PDFVersion = "1.5";
1031  
1032              $this->encoding = $encoding;
1033              $this->b = 0;
1034              $this->i = 0;
1035              $this->u = 0;
1036              $this->HREF = '';
1037              $this->fontlist = array("arial", "times", "courier", "helvetica", "symbol");
1038              $this->issetfont = false;
1039              $this->issetcolor = false;
1040              $this->tableborder = 0;
1041              $this->tdbegin = false;
1042              $this->tdwidth=  0;
1043              $this->tdheight = 0;
1044              if($this->rtl) {
1045                  $this->tdalign = "R";
1046              } else {
1047                  $this->tdalign = "L";
1048              }
1049              $this->tdbgcolor = false;
1050  
1051              $this->SetFillColor(200, 200, 200, true);
1052              $this->SetTextColor(0, 0, 0, true);
1053          }
1054  
1055          /**
1056          * Enable or disable Right-To-Left language mode
1057          * @param Boolean $enable if true enable Right-To-Left language mode.
1058          * @since 2.0.000 (2008-01-03)
1059          */
1060  		function setRTL($enable) {
1061              $this->rtl = $enable ? true : false;
1062              $this->tmprtl = false;
1063          }
1064  
1065          /**
1066          * Force temporary RTL language direction
1067          * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1068          * @since 2.1.000 (2008-01-09)
1069          */
1070  		function setTempRTL($mode) {
1071              switch ($mode) {
1072                  case false:
1073                  case 'L':
1074                  case 'R': {
1075                      $this->tmprtl = $mode;
1076                  }
1077              }
1078          }
1079  
1080          /**
1081          * Set the last cell height.
1082          * @param float $h cell height.
1083          * @author Nicola Asuni
1084          * @since 1.53.0.TC034
1085          */
1086  		function setLastH($h) {
1087              $this->lasth=$h;
1088          }
1089  
1090          /**
1091          * Set the image scale.
1092          * @param float $scale image scale.
1093          * @author Nicola Asuni
1094          * @since 1.5.2
1095          */
1096  		function setImageScale($scale) {
1097              $this->imgscale=$scale;
1098          }
1099  
1100          /**
1101          * Returns the image scale.
1102          * @return float image scale.
1103          * @author Nicola Asuni
1104          * @since 1.5.2
1105          */
1106  		function getImageScale() {
1107              return $this->imgscale;
1108          }
1109  
1110          /**
1111          * Returns the page width in units.
1112          * @return int page width.
1113          * @author Nicola Asuni
1114          * @since 1.5.2
1115          */
1116  		function getPageWidth() {
1117              return $this->w;
1118          }
1119  
1120          /**
1121          * Returns the page height in units.
1122          * @return int page height.
1123          * @author Nicola Asuni
1124          * @since 1.5.2
1125          */
1126  		function getPageHeight() {
1127              return $this->h;
1128          }
1129  
1130          /**
1131          * Returns the page break margin.
1132          * @return int page break margin.
1133          * @author Nicola Asuni
1134          * @since 1.5.2
1135          */
1136  		function getBreakMargin() {
1137              return $this->bMargin;
1138          }
1139  
1140          /**
1141          * Returns the scale factor (number of points in user unit).
1142          * @return int scale factor.
1143          * @author Nicola Asuni
1144          * @since 1.5.2
1145          */
1146  		function getScaleFactor() {
1147              return $this->k;
1148          }
1149  
1150          /**
1151          * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1152          * @param float $left Left margin.
1153          * @param float $top Top margin.
1154          * @param float $right Right margin. Default value is the left one.
1155          * @since 1.0
1156          * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1157          */
1158  		function SetMargins($left, $top, $right=-1) {
1159              //Set left, top and right margins
1160              $this->lMargin=$left;
1161              $this->tMargin=$top;
1162              if($right==-1) {
1163                  $right=$left;
1164              }
1165              $this->rMargin=$right;
1166          }
1167  
1168          /**
1169          * 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.
1170          * @param float $margin The margin.
1171          * @since 1.4
1172          * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1173          */
1174  		function SetLeftMargin($margin) {
1175              //Set left margin
1176              $this->lMargin=$margin;
1177              if(($this->page > 0) AND ($this->x < $margin)) {
1178                  $this->x = $margin;
1179              }
1180          }
1181  
1182          /**
1183          * Defines the top margin. The method can be called before creating the first page.
1184          * @param float $margin The margin.
1185          * @since 1.5
1186          * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1187          */
1188  		function SetTopMargin($margin) {
1189              //Set top margin
1190              $this->tMargin=$margin;
1191              if(($this->page > 0) AND ($this->y < $margin)) {
1192                  $this->y = $margin;
1193              }
1194          }
1195  
1196          /**
1197          * Defines the right margin. The method can be called before creating the first page.
1198          * @param float $margin The margin.
1199          * @since 1.5
1200          * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1201          */
1202  		function SetRightMargin($margin) {
1203              $this->rMargin=$margin;
1204              if(($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1205                  $this->x = $this->w - $margin;
1206              }
1207          }
1208  
1209          /**
1210          * Set the internal Cell padding.
1211          * @param float $pad internal padding.
1212          * @since 2.1.000 (2008-01-09)
1213          * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1214          */
1215  		function SetCellPadding($pad) {
1216              $this->cMargin=$pad;
1217          }
1218  
1219          /**
1220          * 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.
1221          * @param boolean $auto Boolean indicating if mode should be on or off.
1222          * @param float $margin Distance from the bottom of the page.
1223          * @since 1.0
1224          * @see Cell(), MultiCell(), AcceptPageBreak()
1225          */
1226  		function SetAutoPageBreak($auto, $margin=0) {
1227              //Set auto page break mode and triggering margin
1228              $this->AutoPageBreak = $auto;
1229              $this->bMargin = $margin;
1230              $this->PageBreakTrigger = $this->h - $margin;
1231          }
1232  
1233          /**
1234          * Defines the way the document is to be displayed by the viewer. The zoom level can be set: pages can be displayed entirely on screen, occupy the full width of the window, use real size, be scaled by a specific zooming factor or use viewer default (configured in the Preferences menu of Acrobat). The page layout can be specified too: single at once, continuous display, two columns or viewer default. By default, documents use the full width mode with continuous display.
1235          * @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>
1236          * @param string $layout The page layout. Possible values are:<ul><li>single: displays one page at once</li><li>continuous: displays pages continuously (default)</li><li>two: displays two pages on two columns</li><li>default: uses viewer default mode</li></ul>
1237          * @since 1.2
1238          */
1239  		function SetDisplayMode($zoom, $layout='continuous') {
1240              //Set display mode in viewer
1241              if($zoom=='fullpage' or $zoom=='fullwidth' or $zoom=='real' or $zoom=='default' or !is_string($zoom)) {
1242                  $this->ZoomMode=$zoom;
1243              }
1244              else {
1245                  $this->Error('Incorrect zoom display mode: '.$zoom);
1246              }
1247              if($layout=='single' or $layout=='continuous' or $layout=='two' or $layout=='default') {
1248                  $this->LayoutMode=$layout;
1249              }
1250              else {
1251                  $this->Error('Incorrect layout display mode: '.$layout);
1252              }
1253          }
1254  
1255          /**
1256          * 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.
1257          * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1258          * @param boolean $compress Boolean indicating if compression must be enabled.
1259          * @since 1.4
1260          */
1261  		function SetCompression($compress) {
1262              //Set page compression
1263              if(function_exists('gzcompress')) {
1264                  $this->compress=$compress;
1265              }
1266              else {
1267                  $this->compress=false;
1268              }
1269          }
1270  
1271          /**
1272          * Defines the title of the document.
1273          * @param string $title The title.
1274          * @since 1.2
1275          * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1276          */
1277  		function SetTitle($title) {
1278              //Title of document
1279              $this->title=$title;
1280          }
1281  
1282          /**
1283          * Defines the subject of the document.
1284          * @param string $subject The subject.
1285          * @since 1.2
1286          * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1287          */
1288  		function SetSubject($subject) {
1289              //Subject of document
1290              $this->subject=$subject;
1291          }
1292  
1293          /**
1294          * Defines the author of the document.
1295          * @param string $author The name of the author.
1296          * @since 1.2
1297          * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
1298          */
1299  		function SetAuthor($author) {
1300              //Author of document
1301              $this->author=$author;
1302          }
1303  
1304          /**
1305          * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
1306          * @param string $keywords The list of keywords.
1307          * @since 1.2
1308          * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
1309          */
1310  		function SetKeywords($keywords) {
1311              //Keywords of document
1312              $this->keywords=$keywords;
1313          }
1314  
1315          /**
1316          * Defines the creator of the document. This is typically the name of the application that generates the PDF.
1317          * @param string $creator The name of the creator.
1318          * @since 1.2
1319          * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
1320          */
1321  		function SetCreator($creator) {
1322              //Creator of document
1323              $this->creator=$creator;
1324          }
1325  
1326          /**
1327          * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br />
1328          * <b>Example:</b><br />
1329          * <pre>
1330          * class PDF extends TCPDF {
1331          *     function Footer() {
1332          *         //Go to 1.5 cm from bottom
1333          *         $this->SetY(-15);
1334          *         //Select Arial italic 8
1335          *         $this->SetFont('vera','I',8);
1336          *         //Print current and total page numbers
1337          *         $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C');
1338          *     }
1339          * }
1340          * $pdf=new PDF();
1341          * $pdf->AliasNbPages();
1342          * </pre>
1343          * @param string $alias The alias. Default value: {nb}.
1344          * @since 1.4
1345          * @see PageNo(), Footer()
1346          */
1347  		function AliasNbPages($alias='{nb}') {
1348              //Define an alias for total number of pages
1349              $this->AliasNbPages = $this->_escapetext($alias);
1350          }
1351  
1352          /**
1353          * 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.
1354          * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
1355          * @param string $msg The error message
1356          * @since 1.0
1357          */
1358  		function Error($msg) {
1359              //Fatal error
1360              die('<strong>TCPDF error: </strong>'.$msg);
1361          }
1362  
1363          /**
1364          * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically.
1365          * Note: no page is created by this method
1366          * @since 1.0
1367          * @see AddPage(), Close()
1368          */
1369  		function Open() {
1370              //Begin document
1371              $this->state=1;
1372          }
1373  
1374          /**
1375          * Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document.
1376          * @since 1.0
1377          * @see Open(), Output()
1378          */
1379  		function Close() {
1380              //Terminate document
1381              if($this->state==3) {
1382                  return;
1383              }
1384              if($this->page==0) {
1385                  $this->AddPage();
1386              }
1387              //Page footer
1388              $this->InFooter=true;
1389              $this->Footer();
1390              $this->InFooter=false;
1391              //Close page
1392              $this->_endpage();
1393              //Close document
1394              $this->_enddoc();
1395          }
1396  
1397          /**
1398          * Reset pointer to the last document page.
1399          * @since 2.0.000 (2008-01-04)
1400          * @see setPage(), getPage(), getNumPages()
1401          */
1402  		function lastPage() {
1403              $this->page = count($this->pages);
1404          }
1405  
1406          /**
1407          * Move pointer to the apecified document page.
1408          * @param int $pnum page number
1409          * @since 2.1.000 (2008-01-07)
1410          * @see getPage(), lastpage(), getNumPages()
1411          */
1412  		function setPage($pnum) {
1413              if(($pnum > 0) AND ($pnum <= count($this->pages))) {
1414                  $this->page = $pnum;
1415              }
1416          }
1417  
1418          /**
1419          * Get current document page number.
1420          * @return int page number
1421          * @since 2.1.000 (2008-01-07)
1422          * @see setPage(), lastpage(), getNumPages()
1423          */
1424  		function getPage() {
1425              return $this->page;
1426          }
1427  
1428  
1429          /**
1430          * Get the total number of insered pages.
1431          * @return int number of pages
1432          * @since 2.1.000 (2008-01-07)
1433          * @see setPage(), getPage(), lastpage()
1434          */
1435  		function getNumPages() {
1436              return count($this->pages);
1437          }
1438  
1439          /**
1440          * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header.
1441          * The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width.
1442          * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
1443          * @param string $orientation Page orientation. Possible values are (case insensitive):<ul><li>P or Portrait</li><li>L or Landscape</li></ul> The default value is the one passed to the constructor.
1444          * @since 1.0
1445          * @see TCPDF(), Header(), Footer(), SetMargins()
1446          */
1447  		function AddPage($orientation='') {
1448              if (count($this->pages) > $this->page) {
1449                  // this page has been already added
1450                  $this->page++;
1451                  $this->y = $this->tMargin;
1452                  return;
1453              }
1454              //Start a new page
1455              if($this->state==0) {
1456                  $this->Open();
1457              }
1458              $family=$this->FontFamily;
1459              $style=$this->FontStyle.($this->underline ? 'U' : '');
1460              $size=$this->FontSizePt;
1461              $lw=$this->LineWidth;
1462              $dc=$this->DrawColor;
1463              $fc=$this->FillColor;
1464              $tc=$this->TextColor;
1465              $cf=$this->ColorFlag;
1466              if($this->page>0) {
1467                  //Page footer
1468                  $this->InFooter=true;
1469                  $this->Footer();
1470                  $this->InFooter=false;
1471                  //Close page
1472                  $this->_endpage();
1473              }
1474              //Start new page
1475              $this->_beginpage($orientation);
1476              //Set line cap style to square
1477              $this->_out('2 J');
1478              //Set line width
1479              $this->LineWidth=$lw;
1480              $this->_out(sprintf('%.2f w',$lw*$this->k));
1481              //Set font
1482              if($family) {
1483                  $this->SetFont($family,$style,$size);
1484              }
1485              //Set colors
1486              $this->DrawColor=$dc;
1487              if($dc!='0 G') {
1488                  $this->_out($dc);
1489              }
1490              $this->FillColor=$fc;
1491              if($fc!='0 g') {
1492                  $this->_out($fc);
1493              }
1494              $this->TextColor=$tc;
1495              $this->ColorFlag=$cf;
1496              //Page header
1497              $this->Header();
1498              //Restore line width
1499              if($this->LineWidth!=$lw) {
1500                  $this->LineWidth=$lw;
1501                  $this->_out(sprintf('%.2f w',$lw*$this->k));
1502              }
1503              //Restore font
1504              if($family) {
1505                  $this->SetFont($family,$style,$size);
1506              }
1507              //Restore colors
1508              if($this->DrawColor!=$dc) {
1509                  $this->DrawColor=$dc;
1510                  $this->_out($dc);
1511              }
1512              if($this->FillColor!=$fc) {
1513                  $this->FillColor=$fc;
1514                  $this->_out($fc);
1515              }
1516              $this->TextColor=$tc;
1517              $this->ColorFlag=$cf;
1518          }
1519  
1520          /**
1521            * Set header data.
1522           * @param string $ln header image logo
1523           * @param string $lw header image logo width in mm
1524           * @param string $ht string to print as title on document header
1525           * @param string $hs string to print on document header
1526          */
1527  		function setHeaderData($ln="", $lw=0, $ht="", $hs="") {
1528              $this->header_logo = $ln;
1529              $this->header_logo_width = $lw;
1530              $this->header_title = $ht;
1531              $this->header_string = $hs;
1532          }
1533  
1534          /**
1535            * Set header margin.
1536           * (minimum distance between header and top page margin)
1537           * @param int $hm distance in millimeters
1538          */
1539  		function setHeaderMargin($hm=10) {
1540              $this->header_margin = $hm;
1541          }
1542  
1543          /**
1544            * Set footer margin.
1545           * (minimum distance between footer and bottom page margin)
1546           * @param int $fm distance in millimeters
1547          */
1548  		function setFooterMargin($fm=10) {
1549              $this->footer_margin = $fm;
1550          }
1551  
1552          /**
1553            * Set a flag to print page header.
1554           * @param boolean $val set to true to print the page header (default), false otherwise.
1555          */
1556  		function setPrintHeader($val=true) {
1557              $this->print_header = $val;
1558          }
1559  
1560          /**
1561            * Set a flag to print page footer.
1562           * @param boolean $value set to true to print the page footer (default), false otherwise.
1563          */
1564  		function setPrintFooter($val=true) {
1565              $this->print_footer = $val;
1566          }
1567  
1568          /**
1569            * This method is used to render the page header.
1570            * It is automatically called by AddPage() and could be overwritten in your own inherited class.
1571           */
1572  		function Header() {
1573              if ($this->print_header) {
1574  
1575                  if (!isset($this->original_lMargin)) {
1576                      $this->original_lMargin = $this->lMargin;
1577                  }
1578                  if (!isset($this->original_rMargin)) {
1579                      $this->original_rMargin = $this->rMargin;
1580                  }
1581  
1582                  // reset original header margins
1583                  $this->rMargin = $this->original_rMargin;
1584                  $this->lMargin = $this->original_lMargin;
1585  
1586                  // save current font values
1587                  $font_family =  $this->FontFamily;
1588                  $font_style = $this->FontStyle;
1589                  $font_size = $this->FontSizePt;
1590  
1591                  //set current position
1592                  if ($this->rtl) {
1593                      $this->SetXY($this->original_rMargin, $this->header_margin);
1594                  } else {
1595                      $this->SetXY($this->original_lMargin, $this->header_margin);
1596                  }
1597  
1598                  if (($this->header_logo) AND ($this->header_logo != K_BLANK_IMAGE)) {
1599                      $this->Image(K_PATH_IMAGES.$this->header_logo, $this->GetX(), $this->header_margin, $this->header_logo_width);
1600                  } else {
1601                      $this->img_rb_x = $this->GetX();
1602                      $this->img_rb_y = $this->GetY();
1603                  }
1604  
1605                  $cell_height = round((K_CELL_HEIGHT_RATIO * $this->header_font[2]) / $this->k, 2);
1606                  // set starting margin for text data cell
1607                  if ($this->rtl) {
1608                      $header_x = $this->original_rMargin + ($this->header_logo_width * 1.1);
1609                  } else {
1610                      $header_x = $this->original_lMargin + ($this->header_logo_width * 1.1);
1611                  }
1612  
1613                  // header title
1614                  $this->SetFont($this->header_font[0], 'B', $this->header_font[2] + 1);
1615                  $this->SetX($header_x);
1616                  $this->Cell($this->header_width, $cell_height, $this->header_title, 0, 1, '');
1617  
1618                  // header string
1619                  $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
1620                  $this->SetX($header_x);
1621                  $this->MultiCell($this->header_width, $cell_height, $this->header_string, 0, '', 0, 1, 0, 0, true, 0);
1622  
1623                  // print an ending header line
1624                  //set style for cell border
1625                  $prevlinewidth = $this->GetLineWidth();
1626                  $line_width = 0.3;
1627                  $this->SetLineWidth($line_width);
1628                  $this->SetDrawColor(0, 0, 0);
1629                  $this->SetY(1 + max($this->img_rb_y, $this->GetY()));
1630                  if ($this->rtl) {
1631                      $this->SetX($this->original_rMargin);
1632                  } else {
1633                      $this->SetX($this->original_lMargin);
1634                  }
1635                  $this->Cell(0, 0, '', 'T', 0, 'C');
1636                  $this->SetLineWidth($prevlinewidth);
1637  
1638                  //restore position
1639                  if ($this->rtl) {
1640                      $this->SetXY($this->original_rMargin, $this->tMargin);
1641                  } else {
1642                      $this->SetXY($this->original_lMargin, $this->tMargin);
1643                  }
1644  
1645                  // restore font values
1646                  $this->SetFont($font_family, $font_style, $font_size);
1647              }
1648          }
1649  
1650          /**
1651            * This method is used to render the page footer.
1652            * It is automatically called by AddPage() and could be overwritten in your own inherited class.
1653           */
1654  		function Footer() {
1655              if ($this->print_footer) {
1656  
1657                  if (!isset($this->original_lMargin)) {
1658                      $this->original_lMargin = $this->lMargin;
1659                  }
1660                  if (!isset($this->original_rMargin)) {
1661                      $this->original_rMargin = $this->rMargin;
1662                  }
1663  
1664                  // reset original header margins
1665                  $this->rMargin = $this->original_rMargin;
1666                  $this->lMargin = $this->original_lMargin;
1667  
1668                  // save current font values
1669                  $font_family =  $this->FontFamily;
1670                  $font_style = $this->FontStyle;
1671                  $font_size = $this->FontSizePt;
1672  
1673                  //set font
1674                  $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]);
1675                  //set style for cell border
1676                  $prevlinewidth = $this->GetLineWidth();
1677                  $line_width = 0.3;
1678                  $this->SetLineWidth($line_width);
1679                  $this->SetDrawColor(0, 0, 0);
1680  
1681                  $footer_height = round((K_CELL_HEIGHT_RATIO * $this->footer_font[2]) / $this->k, 2); //footer height
1682                  //get footer y position
1683                  $footer_y = $this->h - $this->footer_margin - $footer_height;
1684                  //set current position
1685                  if ($this->rtl) {
1686                      $this->SetXY($this->original_rMargin, $footer_y);
1687                  } else {
1688                      $this->SetXY($this->original_lMargin, $footer_y);
1689                  }
1690  
1691                  //print document barcode
1692                  if ($this->barcode) {
1693                      $this->Ln();
1694                      $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin)/3); //max width
1695                      $this->writeBarcode($this->GetX(), $footer_y + $line_width, $barcode_width, $footer_height - $line_width, "C128B", false, false, 2, $this->barcode);
1696                  }
1697  
1698                  $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / {nb}';
1699  
1700                  $this->SetY($footer_y);
1701  
1702                  //Print page number
1703                  if ($this->rtl) {
1704                      $this->SetX($this->original_rMargin);
1705                      $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'L');
1706                  } else {
1707                      $this->SetX($this->original_lMargin);
1708                      $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'R');
1709                  }
1710                  // restore line width
1711                  $this->SetLineWidth($prevlinewidth);
1712  
1713                  // restore font values
1714                  $this->SetFont($font_family, $font_style, $font_size);
1715              }
1716          }
1717  
1718          /**
1719          * Returns the current page number.
1720          * @return int page number
1721          * @since 1.0
1722          * @see AliasNbPages()
1723          */
1724  		function PageNo() {
1725              //Get current page number
1726              return $this->page;
1727          }
1728  
1729          /**
1730          * 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.
1731          * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255
1732          * @param int $g Green component (between 0 and 255)
1733          * @param int $b Blue component (between 0 and 255)
1734          * @since 1.3
1735          * @see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
1736          */
1737  		function SetDrawColor($r, $g=-1, $b=-1) {
1738              //Set color for all stroking operations
1739              if(($r==0 and $g==0 and $b==0) or $g==-1) {
1740                  $this->DrawColor=sprintf('%.3f G',$r/255);
1741              }
1742              else {
1743                  $this->DrawColor=sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255);
1744              }
1745              if($this->page>0) {
1746                  $this->_out($this->DrawColor);
1747              }
1748          }
1749  
1750          /**
1751          * 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.
1752          * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255
1753          * @param int $g Green component (between 0 and 255)
1754          * @param int $b Blue component (between 0 and 255)
1755          * @param boolean $storeprev if true stores the RGB array on $prevFillColor variable.
1756          * @since 1.3
1757          * @see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
1758          */
1759  		function SetFillColor($r, $g=-1, $b=-1, $storeprev=false) {
1760              //Set color for all filling operations
1761              if(($r==0 and $g==0 and $b==0) or $g==-1) {
1762                  $this->FillColor=sprintf('%.3f g',$r/255);
1763              }
1764              else {
1765                  $this->FillColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255);
1766              }
1767              $this->ColorFlag=($this->FillColor!=$this->TextColor);
1768              if($this->page>0) {
1769                  $this->_out($this->FillColor);
1770              }
1771              if ($storeprev) {
1772                  // store color as previous value
1773                  $this->prevFillColor = array($r, $g, $b);
1774              }
1775          }
1776  
1777          /**
1778          * 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.
1779          * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255
1780          * @param int $g Green component (between 0 and 255)
1781          * @param int $b Blue component (between 0 and 255)
1782          * @param boolean $storeprev if true stores the RGB array on $prevTextColor variable.
1783          * @since 1.3
1784          * @see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
1785          */
1786  		function SetTextColor($r, $g=-1, $b=-1, $storeprev=false) {
1787              //Set color for text
1788              if(($r==0 and $g==0 and $b==0) or $g==-1) {
1789                  $this->TextColor=sprintf('%.3f g',$r/255);
1790              }
1791              else {
1792                  $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255);
1793              }
1794              $this->ColorFlag=($this->FillColor!=$this->TextColor);
1795              if ($storeprev) {
1796                  // store color as previous value
1797                  $this->prevTextColor = array($r, $g, $b);
1798              }
1799          }
1800  
1801          /**
1802          * Returns the length of a string in user unit. A font must be selected.<br>
1803          * @param string $s The string whose length is to be computed
1804          * @return int string length
1805          * @author Nicola Asuni
1806          * @since 1.2
1807          */
1808  		function GetStringWidth($s) {
1809              return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $this->tmprtl));
1810          }
1811  
1812          /**
1813          * Returns the string length of an array of chars in user unit. A font must be selected.<br>
1814          * @param string $arr The array of chars whose total length is to be computed
1815          * @return int string length
1816          * @author Nicola Asuni
1817          * @since 2.4.000 (2008-03-06)
1818          */
1819  		function GetArrStringWidth($sa) {
1820              $w = 0;
1821              foreach($sa as $char) {
1822                  $w += $this->GetCharWidth($char);
1823              }
1824              return $w;
1825          }
1826  
1827          /**
1828          * Returns the length of the char in user unit. A font must be selected.<br>
1829          * @param string $char The char whose length is to be returned
1830          * @return int char width
1831          * @author Nicola Asuni
1832          * @since 2.4.000 (2008-03-06)
1833          */
1834  		function GetCharWidth($char) {
1835              $cw = &$this->CurrentFont['cw'];
1836              if (isset($cw[$char])) {
1837                  $w = $cw[$char];
1838              } elseif(isset($cw[ord($char)])) {
1839                  $w = $cw[ord($char)];
1840              } elseif(isset($cw[chr($char)])) {
1841                  $w = $cw[chr($char)];
1842              } elseif(isset($this->CurrentFont['desc']['MissingWidth'])) {
1843                  $w = $this->CurrentFont['desc']['MissingWidth']; // set default size
1844              } else {
1845                  $w = 500;
1846              }
1847              return ($w * $this->FontSize / 1000);
1848          }
1849  
1850          /**
1851          * Returns the numbero of characters in a string.
1852          * @param string $s The input string.
1853          * @return int number of characters
1854          * @since 2.0.0001 (2008-01-07)
1855          */
1856  		function GetNumChars($s) {
1857              if($this->isunicode) {
1858                  return count($this->UTF8StringToArray($s));
1859              }
1860              return strlen($s);
1861          }
1862  
1863          /**
1864          * Imports a TrueType or Type1 font and makes it available. It is necessary to generate a font definition file first with the makefont.php utility. 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.
1865          * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02].
1866          * <b>Example</b>:<br />
1867          * <pre>
1868          * $pdf->AddFont('Comic','I');
1869          * // is equivalent to:
1870          * $pdf->AddFont('Comic','I','comici.php');
1871          * </pre>
1872          * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
1873          * @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>
1874          * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space.
1875          * @since 1.5
1876          * @see SetFont()
1877          */
1878  		function AddFont($family, $style='', $file='') {
1879              if(empty($family)) {
1880                  return;
1881              }
1882  
1883              //Add a TrueType or Type1 font
1884              $family = strtolower($family);
1885              if((!$this->isunicode) AND ($family == 'arial')) {
1886                  $family = 'helvetica';
1887              }
1888  
1889              $style=strtoupper($style);
1890              $style=str_replace('U','',$style);
1891              if($style == 'IB') {
1892                  $style = 'BI';
1893              }
1894  
1895              $fontkey = $family.$style;
1896              // check if the font has been already added
1897              if(isset($this->fonts[$fontkey])) {
1898                  return;
1899              }
1900  
1901              if($file=='') {
1902                  $file = str_replace(' ', '', $family).strtolower($style).'.php';
1903              }
1904              if(!file_exists($this->_getfontpath().$file)) {
1905                  // try to load the basic file without styles
1906                  $file = str_replace(' ', '', $family).'.php';
1907              }
1908  
1909              include($this->_getfontpath().$file);
1910  
1911              if(!isset($name) AND !isset($fpdf_charwidths)) {
1912                  $this->Error('Could not include font definition file');
1913              }
1914  
1915              $i = count($this->fonts)+1;
1916  
1917              if($this->isunicode) {
1918                  $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg);
1919                  $fpdf_charwidths[$fontkey] = $cw;
1920              } else {
1921                  $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]);
1922              }
1923  
1924              if(isset($diff) AND (!empty($diff))) {
1925                  //Search existing encodings
1926                  $d=0;
1927                  $nb=count($this->diffs);
1928                  for($i=1;$i<=$nb;$i++) {
1929                      if($this->diffs[$i]==$diff) {
1930                          $d=$i;
1931                          break;
1932                      }
1933                  }
1934                  if($d==0) {
1935                      $d=$nb+1;
1936                      $this->diffs[$d]=$diff;
1937                  }
1938                  $this->fonts[$fontkey]['diff']=$d;
1939              }
1940              if(!empty($file)) {
1941                  if((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) {
1942                      $this->FontFiles[$file]=array('length1'=>$originalsize);
1943                  }
1944                  else {
1945                      $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2);
1946                  }
1947              }
1948          }
1949  
1950          /**
1951          * Sets the font used to print character strings. It is mandatory to call this method at least once before printing text or the resulting document would not be valid.
1952          * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
1953          * The method can be called before the first page is created and the font is retained from page to page.
1954          If you just wish to change the current font size, it is simpler to call SetFontSize().
1955          * 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 />
1956          * Example for the last case (note the trailing slash):<br />
1957          * <pre>
1958          * define('K_PATH_FONTS','/home/www/font/');
1959          * require('tcpdf.php');
1960          *
1961          * //Times regular 12
1962          * $pdf->SetFont('Times');
1963          * //Arial bold 14
1964          * $pdf->SetFont('vera','B',14);
1965          * //Removes bold
1966          * $pdf->SetFont('');
1967          * //Times bold, italic and underlined 14
1968          * $pdf->SetFont('Times','BIU');
1969          * </pre><br />
1970          * If the file corresponding to the requested font is not found, the error "Could not include font metric file" is generated.
1971          * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard families (case insensitive):<ul><li>Courier (fixed-width)</li><li>Helvetica or Arial (synonymous; sans serif)</li><li>Times (serif)</li><li>Symbol (symbolic)</li><li>ZapfDingbats (symbolic)</li></ul>It is also possible to pass an empty string. In that case, the current family is retained.
1972          * @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></ul>or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats
1973          * @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
1974          * @since 1.0
1975          * @see AddFont(), SetFontSize()
1976          */
1977  		function SetFont($family, $style='', $size=0) {
1978              // save previous values
1979              $this->prevFontFamily = $this->FontFamily;
1980              $this->prevFontStyle = $this->FontStyle;
1981  
1982              //Select a font; size given in points
1983              global $fpdf_charwidths;
1984  
1985              $family=strtolower($family);
1986              if($family=='') {
1987                  $family=$this->FontFamily;
1988              }
1989              if((!$this->isunicode) AND ($family == 'arial')) {
1990                  $family = 'helvetica';
1991              }
1992              elseif(($family=="symbol") OR ($family=="zapfdingbats")) {
1993                  $style='';
1994              }
1995              $style=strtoupper($style);
1996  
1997              if(strpos($style,'U')!==false) {
1998                  $this->underline=true;
1999                  $style=str_replace('U','',$style);
2000              }
2001              else {
2002                  $this->underline=false;
2003              }
2004              if($style=='IB') {
2005                  $style='BI';
2006              }
2007              if($size==0) {
2008                  $size=$this->FontSizePt;
2009              }
2010  
2011              // try to add font (if not already added)
2012              if($this->isunicode) {
2013                  $this->AddFont($family, $style);
2014              }
2015  
2016              //Test if font is already selected
2017              if(($this->FontFamily == $family) AND ($this->FontStyle == $style) AND ($this->FontSizePt == $size)) {
2018                  return;
2019              }
2020  
2021              $fontkey = $family.$style;
2022              //if(!isset($this->fonts[$fontkey]) AND isset($this->fonts[$family])) {
2023              //    $style='';
2024              //}
2025  
2026              //Test if used for the first time
2027              if(!isset($this->fonts[$fontkey])) {
2028                  //Check if one of the standard fonts
2029                  if(isset($this->CoreFonts[$fontkey])) {
2030                      if(!isset($fpdf_charwidths[$fontkey])) {
2031                          //Load metric file
2032                          $file = $family;
2033                          if(($family!='symbol') AND ($family!='zapfdingbats')) {
2034                              $file .= strtolower($style);
2035                          }
2036                          if(!file_exists($this->_getfontpath().$file.'.php')) {
2037                              // try to load the basic file without styles
2038                              $file = $family;
2039                              $fontkey = $family;
2040                          }
2041                          include($this->_getfontpath().$file.'.php');
2042                          if (($this->isunicode AND !isset($ctg)) OR ((!$this->isunicode) AND (!isset($fpdf_charwidths[$fontkey]))) ) {
2043                              $this->Error("Could not include font metric file [".$fontkey."]: ".$this->_getfontpath().$file.".php");
2044                          }
2045                      }
2046                      $i = count($this->fonts) + 1;
2047  
2048                      if($this->isunicode) {
2049                          $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg);
2050                          $fpdf_charwidths[$fontkey] = $cw;
2051                      } else {
2052                          $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]);
2053                      }
2054                  }
2055                  else {
2056                      $this->Error('Undefined font: '.$family.' '.$style);
2057                  }
2058              }
2059              //Select it
2060              $this->FontFamily = $family;
2061              $this->FontStyle = $style;
2062              $this->FontSizePt = $size;
2063              $this->FontSize = $size / $this->k;
2064              $this->CurrentFont = &$this->fonts[$fontkey];
2065              if($this->page>0) {
2066                  $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
2067              }
2068          }
2069  
2070          /**
2071          * Defines the size of the current font.
2072          * @param float $size The size (in points)
2073          * @since 1.0
2074          * @see SetFont()
2075          */
2076  		function SetFontSize($size) {
2077              //Set font size in points
2078              if($this->FontSizePt==$size) {
2079                  return;
2080              }
2081              $this->FontSizePt = $size;
2082              $this->FontSize = $size / $this->k;
2083              if($this->page > 0) {
2084                  $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
2085              }
2086          }
2087  
2088          /**
2089          * 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 />
2090          * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
2091          * @since 1.5
2092          * @see Cell(), Write(), Image(), Link(), SetLink()
2093          */
2094  		function AddLink() {
2095              //Create a new internal link
2096              $n=count($this->links)+1;
2097              $this->links[$n]=array(0,0);
2098              return $n;
2099          }
2100  
2101          /**
2102          * Defines the page and position a link points to
2103          * @param int $link The link identifier returned by AddLink()
2104          * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
2105          * @param int $page Number of target page; -1 indicates the current page. This is the default value
2106          * @since 1.5
2107          * @see AddLink()
2108          */
2109  		function SetLink($link, $y=0, $page=-1) {
2110              //Set destination of internal link
2111              if($y==-1) {
2112                  $y=$this->y;
2113              }
2114              if($page==-1) {
2115                  $page=$this->page;
2116              }
2117              $this->links[$link]=array($page,$y);
2118          }
2119  
2120          /**
2121          * Puts a link on a rectangular area of the page. 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.
2122          * @param float $x Abscissa of the upper-left corner of the rectangle (or upper-right for RTL languages)
2123          * @param float $y Ordinate of the upper-left corner of the rectangle (or upper-right for RTL languages)
2124          * @param float $w Width of the rectangle
2125          * @param float $h Height of the rectangle
2126          * @param mixed $link URL or identifier returned by AddLink()
2127          * @since 1.5
2128          * @see AddLink(), Cell(), Write(), Image()
2129          */
2130  		function Link($x, $y, $w, $h, $link) {
2131              $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h*$this->k, $link);
2132          }
2133  
2134          /**
2135          * Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text.
2136          * @param float $x Abscissa of the origin
2137          * @param float $y Ordinate of the origin
2138          * @param string $txt String to print
2139          * @since 1.0
2140          * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write()
2141          */
2142  		function Text($x, $y, $txt) {
2143              //Output a string
2144              if($this->rtl) {
2145                  // bidirectional algorithm (some chars may be changed affecting the line length)
2146                  $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $this->tmprtl);
2147                  $l = $this->GetArrStringWidth($s);
2148                  $xr = $this->w - $x - $this->GetArrStringWidth($s);
2149              } else {
2150                  $xr = $x;
2151              }
2152              $s = sprintf('BT %.2f %.2f Td (%s) Tj ET', $xr * $this->k, ($this->h-$y) * $this->k, $this->_escapetext($txt));
2153              if($this->underline AND ($txt!='')) {
2154                  $s .= ' '.$this->_dounderline($xr, $y, $txt);
2155              }
2156              if($this->ColorFlag) {
2157                  $s='q '.$this->TextColor.' '.$s.' Q';
2158              }
2159              $this->_out($s);
2160          }
2161  
2162          /**
2163          * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
2164          * This method is called automatically and should not be called directly by the application.<br />
2165          * <b>Example:</b><br />
2166          * The method is overriden in an inherited class in order to obtain a 3 column layout:<br />
2167          * <pre>
2168          * class PDF extends TCPDF {
2169          *     var $col=0;
2170          *
2171          *     function SetCol($col) {
2172          *         //Move position to a column
2173          *         $this->col=$col;
2174          *         $x=10+$col*65;
2175          *         $this->SetLeftMargin($x);
2176          *         $this->SetX($x);
2177          *     }
2178          *
2179          *     function AcceptPageBreak() {
2180          *         if($this->col<2) {
2181          *             //Go to next column
2182          *             $this->SetCol($this->col+1);
2183          *             $this->SetY(10);
2184          *             return false;
2185          *         }
2186          *         else {
2187          *             //Go back to first column and issue page break
2188          *             $this->SetCol(0);
2189          *             return true;
2190          *         }
2191          *     }
2192          * }
2193          *
2194          * $pdf=new PDF();
2195          * $pdf->Open();
2196          * $pdf->AddPage();
2197          * $pdf->SetFont('vera','',12);
2198          * for($i=1;$i<=300;$i++) {
2199          *     $pdf->Cell(0,5,"Line $i",0,1);
2200          * }
2201          * $pdf->Output();
2202          * </pre>
2203          * @return boolean
2204          * @since 1.4
2205          * @see SetAutoPageBreak()
2206          */
2207  		function AcceptPageBreak() {
2208              //Accept automatic page break or not
2209              return $this->AutoPageBreak;
2210          }
2211  
2212          /**
2213          * 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 />
2214          * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2215          * @param float $w Cell width. If 0, the cell extends up to the right margin.
2216          * @param float $h Cell height. Default value: 0.
2217          * @param string $txt String to print. Default value: empty string.
2218          * @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>
2219          * @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>
2220          Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
2221          * @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>
2222          * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2223          * @param mixed $link URL or identifier returned by AddLink().
2224          * @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>
2225          * @since 1.0
2226          * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
2227          */
2228  		function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2229  
2230              $k = $this->k;
2231  
2232              if((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
2233                  //Automatic page break
2234                  $x = $this->x;
2235                  $ws = $this->ws;
2236                  if($ws > 0) {
2237                      $this->ws = 0;
2238                      $this->_out('0 Tw');
2239                  }
2240                  $this->AddPage($this->CurOrientation);
2241                  if($ws > 0) {
2242                      $this->ws = $ws;
2243                      $this->_out(sprintf('%.3f Tw',$ws * $k));
2244                  }
2245                  $this->x = $x;
2246              }
2247              if($w == 0) {
2248                  if ($this->rtl) {
2249                      $w = $this->x - $this->lMargin;
2250                  } else {
2251                      $w = $this->w - $this->rMargin - $this->x;
2252                  }
2253              }
2254              $s = '';
2255              if(($fill == 1) OR ($border == 1)) {
2256                  if($fill == 1) {
2257                      $op = ($border == 1) ? 'B' : 'f';
2258                  } else {
2259                      $op = 'S';
2260                  }
2261                  if ($this->rtl) {
2262                      $xk = ($this->x - $w) * $k;
2263                  } else {
2264                      $xk = $this->x * $k;
2265                  }
2266                  $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op);
2267              }
2268              if(is_string($border)) {
2269                  $x=$this->x;
2270                  $y=$this->y;
2271                  if(strpos($border,'L')!==false) {
2272                      if ($this->rtl) {
2273                          $xk = ($x - $w) * $k;
2274                      } else {
2275                          $xk = $x * $k;
2276                      }
2277                      $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k);
2278                  }
2279                  if(strpos($border,'T')!==false) {
2280                      if ($this->rtl) {
2281                          $xk = ($x - $w) * $k;
2282                          $xwk = $x * $k;
2283                      } else {
2284                          $xk = $x * $k;
2285                          $xwk = ($x + $w) * $k;
2286                      }
2287                      $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xwk,($this->h-$y)*$k);
2288                  }
2289                  if(strpos($border,'R')!==false) {
2290                      if ($this->rtl) {
2291                          $xk = $x * $k;
2292                      } else {
2293                          $xk = ($x + $w) * $k;
2294                      }
2295                      $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k);
2296                  }
2297                  if(strpos($border,'B')!==false) {
2298                      if ($this->rtl) {
2299                          $xk = ($x - $w) * $k;
2300                          $xwk = $x * $k;
2301                      } else {
2302                          $xk = $x * $k;
2303                          $xwk = ($x + $w) * $k;
2304                      }
2305                      $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-($y+$h))*$k,$xwk,($this->h-($y+$h))*$k);
2306                  }
2307              }
2308              if($txt != '') {
2309                  // text lenght
2310                  $width = $this->GetStringWidth($txt);
2311                  // ratio between cell lenght and text lenght
2312                  $ratio = ($w - (2 * $this->cMargin)) / $width;
2313  
2314                  // stretch text if required
2315                  if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
2316                      if ($stretch > 2) {
2317                          // spacing
2318                          //Calculate character spacing in points
2319                          $char_space = ($w - $width - (2 * $this->cMargin)) / max($this->GetNumChars($s)-1,1) * $this->k;
2320                          //Set character spacing
2321                          $this->_out(sprintf('BT %.2f Tc ET', $char_space));
2322                      } else {
2323                          // scaling
2324                          //Calculate horizontal scaling
2325                          $horiz_scale = $ratio*100.0;
2326                          //Set horizontal scaling
2327                          $this->_out(sprintf('BT %.2f Tz ET', $horiz_scale));
2328                      }
2329                      $align = '';
2330                      $width = $w - (2 * $this->cMargin);
2331                  } else {
2332                      $stretch == 0;
2333                  }
2334  
2335                  if($align == 'L') {
2336                      if ($this->rtl) {
2337                          $dx = $w - $width - $this->cMargin;
2338                      } else {
2339                          $dx = $this->cMargin;
2340                      }
2341                  } elseif($align == 'R') {
2342                      if ($this->rtl) {
2343                          $dx = $this->cMargin;
2344                      } else {
2345                          $dx = $w - $width - $this->cMargin;
2346                      }
2347                  } elseif($align=='C') {
2348                      $dx = ($w - $width)/2;
2349                  } elseif($align=='J') {
2350                      if ($this->rtl) {
2351                          $dx = $w - $width - $this->cMargin;
2352                      } else {
2353                          $dx = $this->cMargin;
2354                      }
2355                  } else {
2356                      $dx = $this->cMargin;
2357                  }
2358                  if($this->ColorFlag) {
2359                      $s .= 'q '.$this->TextColor.' ';
2360                  }
2361                  $txt2 = $this->_escapetext($txt);
2362                  if ($this->rtl) {
2363                      $xdk = ($this->x - $dx - $width) * $k;
2364                  } else {
2365                      $xdk = ($this->x + $dx) * $k;
2366                  }
2367                  // 2008-02-16 Jacek Czekaj - multibyte justification
2368                  if ($align == 'J') {
2369                      // count number of spaces
2370                      $ns = substr_count($txt, ' ');
2371                      // get string width without spaces
2372                      $width = $this->GetStringWidth(str_replace(' ', '', $txt));
2373                      // set word position to be used with TJ operator
2374                      $txt2 = str_replace(chr(0).' ', ') '. -2830*($w-$width-(2*$this->cMargin))/($ns?$ns:1)/$this->FontSize/$this->k . ' (', $txt2);
2375                  }
2376  
2377                  $s.=sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2);
2378  
2379                  if($this->underline) {
2380                      if ($this->rtl) {
2381                          $xdx = $this->x - $dx - $width;
2382                      } else {
2383                          $xdx = $this->x + $dx;
2384                      }
2385                      $s.=' '.$this->_dounderline($xdx, $this->y + 0.5 * $h + 0.3 * $this->FontSize, $txt);
2386                  }
2387                  if($this->ColorFlag) {
2388                      $s.=' Q';
2389                  }
2390                  if($link) {
2391                      if ($this->rtl) {
2392                          $xdx = $this->x - $dx - $width;
2393                      } else {
2394                          $xdx = $this->x + $dx;
2395                      }
2396                      $this->Link($xdx, $this->y + 0.5 * $h - 0.5 * $this->FontSize, $width, $this->FontSize, $link);
2397                  }
2398              }
2399  
2400              // output cell
2401              if($s) {
2402                  // output cell
2403                  $this->_out($s);
2404                  // reset text stretching
2405                  if($stretch > 2) {
2406                      //Reset character horizontal spacing
2407                      $this->_out('BT 0 Tc ET');
2408                  } elseif($stretch > 0) {
2409                      //Reset character horizontal scaling
2410                      $this->_out('BT 100 Tz ET');
2411                  }
2412              }
2413  
2414              $this->lasth = $h;
2415  
2416              if($ln>0) {
2417                  //Go to the beginning of the next line
2418                  $this->y += $h;
2419                  if($ln == 1) {
2420                      if ($this->rtl) {
2421                          $this->x = $this->w - $this->rMargin;
2422                      } else {
2423                          $this->x = $this->lMargin;
2424                      }
2425                  }
2426              } else {
2427                  // go left or right by case
2428                  if ($this->rtl) {
2429                      $this->x -= $w;
2430                  } else {
2431                      $this->x += $w;
2432                  }
2433              }
2434          }
2435  
2436          /**
2437          * This method allows printing text with line breaks. 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 />
2438          * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
2439          * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
2440          * @param float $h Cell minimum height. The cell extends automatically if needed.
2441          * @param string $txt String to print
2442          * @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>
2443          * @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)</li></ul>
2444          * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2445          * @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>
2446          * @param int $x x position in user units
2447          * @param int $y y position in user units
2448          * @param boolean $reseth if true reset the last cell height (default true).
2449          * @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>
2450          * @return int Rerurn the number of lines.
2451          * @since 1.3
2452          * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
2453          */
2454  		function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0) {
2455              if ((empty($this->lasth))OR ($reseth)) {
2456                  //set row height
2457                  $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO;
2458              }
2459  
2460              // get current page number
2461              $startpage = $this->page;
2462  
2463              if (!empty($y)) {
2464                  $this->SetY($y);
2465              } else {
2466                  $y = $this->GetY();
2467              }
2468              if (!empty($x)) {
2469                  $this->SetX($x);
2470              } else {
2471                  $x = $this->GetX();
2472              }
2473  
2474              if(empty($w)) {
2475                  if ($this->rtl) {
2476                      $w = $this->x - $this->lMargin;
2477                  } else {
2478                      $w = $this->w - $this->rMargin - $this->x;
2479                  }
2480              }
2481  
2482              // store original margin values
2483              $lMargin = $this->lMargin;
2484              $rMargin = $this->rMargin;
2485  
2486              // set new margin values
2487              if ($this->rtl) {
2488                  $this->SetLeftMargin($this->x - $w);
2489                  $this->SetRightMargin($this->w - $this->x);
2490              } else {
2491                  $this->SetLeftMargin($this->x);
2492                  $this->SetRightMargin($this->w - $this->x - $w);
2493              }
2494  
2495              // calculate remaining vertical space on first page ($startpage)
2496              $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin();
2497  
2498              // Write text
2499              $nl = $this->Write($this->lasth, $txt, '', $fill, $align, true, $stretch);
2500  
2501              // Get end-of-text Y position
2502              $currentY = $this->GetY();
2503              // get latest page number
2504              $endpage = $this->page;
2505  
2506              if (!empty($border)) {
2507                  // check if a new page has been created
2508                  if ($endpage > $startpage) {
2509                      // design borders around HTML cells.
2510                      for ($page=$startpage; $page<=$endpage; $page++) {
2511                          $this->page = $page;
2512                          if ($page==$startpage) {
2513                              $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin());
2514                              $h = $restspace - 1;
2515                          } elseif ($page==$endpage) {
2516                              $this->SetY($this->tMargin); // put cursor at the beginning of text
2517                              $h = $currentY - $this->tMargin;
2518                          } else {
2519                              $this->SetY($this->tMargin); // put cursor at the beginning of text
2520                              $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
2521                          }
2522                          $this->SetX($x);
2523                          $this->Cell($w, $h, "", $border, 1, '', 0);
2524                      }
2525                  } else {
2526                      $h = max($h, ($currentY - $y));
2527                      $this->SetY($y); // put cursor at the beginning of text
2528                      $this->SetX($x);
2529                      // design a cell around the text
2530                      $this->Cell($w, $h, "", $border, 1, '', 0);
2531                  }
2532              }
2533  
2534              // restore original margin values
2535              $this->SetLeftMargin($lMargin);
2536              $this->SetRightMargin($rMargin);
2537  
2538              if($ln>0) {
2539                  //Go to the beginning of the next line
2540                  $this->SetY($currentY);
2541                  if($ln == 2) {
2542                      $this->SetX($x + $w);
2543                  }
2544              } else {
2545                  // go left or right by case
2546                  $this->page = $startpage;
2547                  $this->y = $y;
2548                  $this->SetX($x + $w);
2549              }
2550  
2551              return $nl;
2552          }
2553  
2554          /**
2555          * This method prints text from the current position.<br />
2556          * @param float $h Line height
2557          * @param string $txt String to print
2558          * @param mixed $link URL or identifier returned by AddLink()
2559          * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
2560          * @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>
2561          * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
2562          * @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>
2563          * @return int Rerurn the number of lines.
2564          * @since 1.5
2565          */
2566  		function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0) {
2567  
2568              // store current position
2569              $prevx = $this->x;
2570              $prevy = $this->y;
2571  
2572              // Adjust internal padding
2573              if ($this->cMargin < ($this->LineWidth/2)) {
2574                  $this->cMargin = ($this->LineWidth/2);
2575              }
2576  
2577              // Add top space if needed
2578              if (($h - $this->FontSize) < $this->LineWidth) {
2579                  $this->y += $this->LineWidth/2;
2580              }
2581  
2582              //if ($h < ($this->LineWidth)) {
2583              //    $h = ($this->LineWidth);
2584              //}
2585  
2586              // calculating remaining line width ($w)
2587              if ($this->rtl) {
2588                  $w = $this->x - $this->lMargin;
2589              } else {
2590                  $w = $this->w - $this->rMargin - $this->x;
2591              }
2592  
2593              // remove carriage returns
2594              $s = str_replace("\r", '', $txt);
2595  
2596              // get array of chars
2597              $chars = $this->UTF8StringToArray($s);
2598  
2599              // get the number of characters
2600              $nb = count($chars);
2601  
2602              // handle single space character
2603              if(($nb==1) AND preg_match("/[\s]/u", $s)) {
2604                  if ($this->rtl) {
2605                      $this->x -= $this->GetStringWidth($s);
2606                  } else {
2607                      $this->x += $this->GetStringWidth($s);
2608                  }
2609                  return;
2610              }
2611  
2612              // max column width
2613              $wmax = $w - (2 * $this->cMargin);
2614  
2615              $i = 0; // character position
2616              $j = 0; // current srting starting position
2617              $sep = -1; // position of the last blank space
2618              $l = 0; // current string lenght
2619              $nl = 0; //number of lines
2620  
2621              // for each character
2622              while($i < $nb) {
2623                  //Get the current character
2624                  $c = $chars[$i];
2625                  if ($c == 10) {
2626                      // 10 = "\n" = new line
2627                      //Explicit line break
2628                      if ($align == "J") {
2629                          if ($this->rtl) {
2630                              $talign = "R";
2631                          } else {
2632                              $talign = "L";
2633                          }
2634                      } else {
2635                          $talign = $align;
2636                      }
2637                      $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 2, $talign, $fill, $link, $stretch);
2638                      $nl++;
2639                      $j = $i + 1;
2640                      $l = 0;
2641                      $sep = -1;
2642                      if($nl == 1) {
2643                          // set the next line width and position
2644                          if ($this->rtl) {
2645                              $this->x = $this->w - $this->rMargin;
2646                              $w = $this->x - $this->lMargin;
2647                          }
2648                          else {
2649                              $this->x = $this->lMargin;
2650                              $w = $this->w - $this->rMargin - $this->x;
2651                          }
2652                          $wmax = $w - (2 * $this->cMargin);
2653                      }
2654                  } else {
2655                      if(preg_match("/[\s]/u", $this->unichr($c))) {
2656                          // update last blank space position
2657                          $sep = $i;
2658                      }
2659  
2660                      // update string length
2661                      if($this->isunicode) {
2662                          // with bidirectional algorithm some chars may be changed affecting the line length
2663                          // *** very slow
2664                          $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), $this->tmprtl));
2665                      } else {
2666                          $l += $this->GetCharWidth($c);
2667                      }
2668  
2669                      if($l > $wmax) {
2670                          // we have reached the end of column
2671                          if($sep == -1) {
2672                              // truncate the word because do not fit on column
2673                              $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 2, $align, $fill, $link, $stretch);
2674                              $nl++;
2675                              if($nl == 1) {
2676                                  // set the next line width and position
2677                                  if ($this->rtl) {
2678                                      $this->x = $this->w - $this->rMargin;
2679                                      $w = $this->x - $this->lMargin;
2680                                  }
2681                                  else {
2682                                      $this->x = $this->lMargin;
2683                                      $w = $this->w - $this->rMargin - $this->x;
2684                                  }
2685                                  $wmax = $w - (2 * $this->cMargin);
2686                              }
2687                          } else {
2688                              // word wrapping
2689                              $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $sep), 0, 2, $align, $fill, $link, $stretch);
2690                              $nl++;
2691                              $i = $sep + 1;
2692                              if($nl == 1) {
2693                                  // set the next line width and position
2694                                  if ($this->rtl) {
2695                                      $this->x = $this->w - $this->rMargin;
2696                                      $w = $this->x - $this->lMargin;
2697                                  }
2698                                  else {
2699                                      $this->x = $this->lMargin;
2700                                      $w = $this->w - $this->rMargin - $this->x;
2701                                  }
2702                                  $wmax = $w - (2 * $this->cMargin);
2703                              }
2704                          }
2705                          $sep = -1;
2706                          $j = $i;
2707                          $l = 0;
2708                      }
2709                  }
2710                  $i++;
2711              } // end while i < nb
2712              // print last row
2713              if($i != $j) {
2714                  $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $nb), 0, $ln, $align, $fill, $link, $stretch);
2715                  $nl++;
2716              }
2717  
2718              $w = $this->GetStringWidth($this->UTF8ArrSubString($chars, $j, $nb)) + (2 * $this->cMargin);
2719              if ($this->rtl) {
2720                  $this->x = $prevx - $w;
2721              } else {
2722                  $this->x = $prevx + $w;
2723              }
2724  
2725              // Add bottom space if needed
2726              if (($ln > 0) AND (($h - $this->FontSize) < $this->LineWidth)) {
2727                  $this->y += $this->LineWidth/2;
2728              }
2729  
2730              return $nl;
2731          }
2732  
2733          /**
2734          * Extract a slice of the $strarr array and return it as string.
2735          * @param string $strarr The input array of characters.
2736          * @param int $start the starting element of $strarr.
2737          * @param int $end first element that will not be returned.
2738          * @return Return part of a string
2739          */
2740  		function UTF8ArrSubString($strarr, $start='', $end='') {
2741              if (strlen($start) == 0) {
2742                  $start = 0;
2743              }
2744              if (strlen($end) == 0) {
2745                  $end = count($strarr);
2746              }
2747              $string = "";
2748              for ($i=$start; $i < $end; $i++) {
2749                  $string .= $this->unichr($strarr[$i]);
2750              }
2751              return $string;
2752          }
2753  
2754          /**
2755          * Returns the unicode caracter specified by UTF-8 code
2756          * @param int $c UTF-8 code
2757          * @return Returns the specified character.
2758          * @author Miguel Perez, Nicola Asuni
2759          * @since 2.3.000 (2008-03-05)
2760          */
2761  		function unichr($c) {
2762              if (!$this->isunicode) {
2763                  return chr($c);
2764              } elseif ($c <= 0x7F) {
2765                  // one byte
2766                  return chr($c);
2767              } else if ($c <= 0x7FF) {
2768                  // two bytes
2769                  return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
2770              } else if ($c <= 0xFFFF) {
2771                  // three bytes
2772                  return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
2773              } else if ($c <= 0x10FFFF) {
2774                  // four bytes
2775                  return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
2776              } else {
2777                  return "";
2778              }
2779          }
2780  
2781          /**
2782          * Puts an image in the page. The upper-left corner must be given. The dimensions can be specified in different ways:<ul><li>explicit width and height (expressed in user unit)</li><li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li><li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
2783          * Supported formats are JPEG and PNG.
2784          * For JPEG, all flavors are allowed:<ul><li>gray scales</li><li>true colors (24 bits)</li><li>CMYK (32 bits)</li></ul>
2785          * For PNG, are allowed:<ul><li>gray scales on at most 8 bits (256 levels)</li><li>indexed colors</li><li>true colors (24 bits)</li></ul>
2786          * If a transparent color is defined, it will be taken into account (but will be only interpreted by Acrobat 4 and above).<br />
2787          * The format can be specified explicitly or inferred from the file extension.<br />
2788          * It is possible to put a link on the image.<br />
2789          * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
2790          * @param string $file Name of the file containing the image.
2791          * @param float $x Abscissa of the upper-left corner.
2792          * @param float $y Ordinate of the upper-left corner.
2793          * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
2794          * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
2795          * @param string $type Image format. Possible values are (case insensitive): JPG, JPEG, PNG. If not specified, the type is inferred from the file extension.
2796          * @param mixed $link URL or identifier returned by AddLink().
2797          * @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>
2798          * @since 1.1
2799          * @see AddLink()
2800          */
2801  		function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='') {
2802              //Put an image on the page
2803              if(!isset($this->images[$file])) {
2804                  //First use of image, get info
2805                  if($type == '') {
2806                      $pos = strrpos($file,'.');
2807                      if(empty($pos)) {
2808                          $this->Error('Image file has no extension and no type was specified: '.$file);
2809                      }
2810                      $type = substr($file, $pos+1);
2811                  }
2812                  $type = strtolower($type);
2813                  $mqr = get_magic_quotes_runtime();
2814                  set_magic_quotes_runtime(0);
2815                  if($type == 'jpg' or $type == 'jpeg') {
2816                      $info=$this->_parsejpg($file);
2817                  } elseif($type == 'gif') {
2818                      $info=$this->_parsegif($file);
2819                  } elseif($type == 'png') {
2820                      $info=$this->_parsepng($file);
2821                  }else {
2822                      //Allow for additional formats
2823                      $mtd='_parse'.$type;
2824                      if(!method_exists($this,$mtd)) {
2825                          $this->Error('Unsupported image type: '.$type);
2826                      }
2827                      $info=$this->$mtd($file);
2828                  }
2829                  if($info === false) {
2830                      //If false, we cannot process image
2831                      return;
2832                  }
2833                  set_magic_quotes_runtime($mqr);
2834                  $info['i']=count($this->images)+1;
2835                  $this->images[$file]=$info;
2836              }
2837              else {
2838                  $info=$this->images[$file];
2839              }
2840              //Automatic width and height calculation if needed
2841              if(($w == 0) and ($h == 0)) {
2842                  //Put image at 72 dpi
2843                  // 2004-06-14 :: Nicola Asuni, scale factor where added
2844                  $w = $info['w'] / ($this->imgscale * $this->k);
2845                  $h = $info['h'] / ($this->imgscale * $this->k);
2846              }
2847              if($w == 0) {
2848                  $w = $h * $info['w'] / $info['h'];
2849              }
2850              if($h == 0) {
2851                  $h = $w * $info['h'] / $info['w'];
2852              }
2853  
2854              // 2007-10-19 Warren Sherliker
2855              // Check whether we need a new page first as this does not fit
2856              // Copied from Cell()
2857              if((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
2858                  // Automatic page break
2859                  $this->AddPage($this->CurOrientation);
2860                  // Reset coordinates to top fo next page
2861                  $x = $this->GetX();
2862                  $y = $this->GetY();
2863              }
2864              // 2007-10-19 Warren Sherliker: End Edit
2865  
2866              // set bottomcoordinates
2867              $this->img_rb_y = $y + $h;
2868              if ($this->rtl) {
2869                  $ximg = ($this->w - $x -$w);
2870                  // set left side coordinate
2871                  $this->img_rb_x = $ximg;
2872              } else {
2873                  $ximg = $x;
2874                  // set right side coordinate
2875                  $this->img_rb_x = $ximg + $w;
2876              }
2877              $xkimg = $ximg * $this->k;
2878              $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']));
2879  
2880              if($link) {
2881                  $this->Link($ximg, $y, $w, $h, $link);
2882              }
2883  
2884              // set pointer to align the successive text/objects
2885              switch($align) {
2886                  case 'T':{
2887                      $this->y = $y;
2888                      $this->x = $this->img_rb_x;
2889                      break;
2890                  }
2891                  case 'M':{
2892                      $this->y = $y + round($h/2);
2893                      $this->x = $this->img_rb_x;
2894                      break;
2895                  }
2896                  case 'B':{
2897                      $this->y = $this->img_rb_y;
2898                      $this->x = $this->img_rb_x;
2899                      break;
2900                  }
2901                  case 'N':{
2902                      $this->SetY($this->img_rb_y);
2903                      break;
2904                  }
2905                  default:{
2906                      break;
2907                  }
2908              }
2909          }
2910  
2911  
2912          /**
2913          * Performs a line break. The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
2914          * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
2915          * @since 1.0
2916          * @see Cell()
2917          */
2918          function Ln($h='') {
2919              //Line feed; default value is last cell height
2920              if ($this->rtl) {
2921                  $this->x = $this->w - $this->rMargin;
2922              } else {
2923                  $this->x = $this->lMargin;
2924              }
2925              if(is_string($h)) {
2926                  $this->y += $this->lasth;
2927              } else {
2928                  $this->y += $h;
2929              }
2930          }
2931  
2932          /**
2933          * Returns the relative X value of current position.
2934          * The value is relative to the left border for LTR languages and to the right border for RTL languages.
2935          * @return float
2936          * @since 1.2
2937          * @see SetX(), GetY(), SetY()
2938          */
2939  		function GetX() {
2940              //Get x position
2941              if ($this->rtl) {
2942                  return ($this->w - $this->x);
2943              } else {
2944                  return $this->x;
2945              }
2946          }
2947  
2948          /**
2949          * Returns the absolute X value of current position.
2950          * @return float
2951          * @since 1.2
2952          * @see SetX(), GetY(), SetY()
2953          */
2954  		function GetAbsX() {
2955              return $this->x;
2956          }
2957  
2958          /**
2959          * Returns the ordinate of the current position.
2960          * @return float
2961          * @since 1.0
2962          * @see SetY(), GetX(), SetX()
2963          */
2964  		function GetY() {
2965              //Get y position
2966              return $this->y;
2967          }
2968  
2969          /**
2970          * Defines the abscissa of the current position.
2971          * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
2972          * @param float $x The value of the abscissa.
2973          * @since 1.2
2974          * @see GetX(), GetY(), SetY(), SetXY()
2975          */
2976  		function SetX($x) {
2977              //Set x position
2978              if ($this->rtl) {
2979                  if($x >= 0) {
2980                      $this->x = $this->w - $x;
2981                  } else {
2982                      $this->x = abs($x);
2983                  }
2984              } else {
2985                  if($x >= 0) {
2986                      $this->x = $x;
2987                  } else {
2988                      $this->x = $this->w + $x;
2989                  }
2990              }
2991          }
2992  
2993          /**
2994          * Moves the current abscissa back to the left margin and sets the ordinate.
2995          * If the passed value is negative, it is relative to the bottom of the page.
2996          * @param float $y The value of the ordinate.
2997          * @since 1.0
2998          * @see GetX(), GetY(), SetY(), SetXY()
2999          */
3000  		function SetY($y) {
3001              //Set y position and reset x
3002              if ($this->rtl) {
3003                  $this->x = $this->w - $this->rMargin;
3004              } else {
3005                  $this->x = $this->lMargin;
3006              }
3007              if($y >= 0) {
3008                  $this->y = $y;
3009              } else {
3010                  $this->y = $this->h + $y;
3011              }
3012          }
3013  
3014  
3015          /**
3016          * Defines the abscissa and ordinate of the current position. If the passed values are negative, they are relative respectively to the right and bottom of the page.
3017          * @param float $x The value of the abscissa
3018          * @param float $y The value of the ordinate
3019          * @since 1.2
3020          * @see SetX(), SetY()
3021          */
3022  		function SetXY($x, $y) {
3023              //Set x and y positions
3024              $this->SetY($y);
3025              $this->SetX($x);
3026          }
3027  
3028          /**
3029          * Send the document to a given destination: string, local file or browser. In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
3030          * The method first calls Close() if necessary to terminate the document.
3031          * @param string $name The name of the file. If not given, the document will be sent to the browser (destination I) with the name doc.pdf.
3032          * @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. 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>If the parameter is not specified but a name is given, destination is F. If no parameter is specified at all, destination is I.<br />Note: for compatibility with previous versions, a boolean value is also accepted (false for F and true for D).
3033          * @since 1.0
3034          * @see Close()
3035          */
3036  		function Output($name='',$dest='') {
3037              //Output PDF to some destination
3038              //Finish document if necessary
3039              if($this->state < 3) {
3040                  $this->Close();
3041              }
3042              //Normalize parameters
3043              if(is_bool($dest)) {
3044                  $dest=$dest ? 'D' : 'F';
3045              }
3046              $dest=strtoupper($dest);
3047              if($dest=='') {
3048                  if($name=='') {
3049                      $name='doc.pdf';
3050                      $dest='I';
3051                  } else {
3052                      $dest='F';
3053                  }
3054              }
3055              switch($dest) {
3056                  case 'I': {
3057                      //Send to standard output
3058                      if(ob_get_contents()) {
3059                          $this->Error('Some data has already been output, can\'t send PDF file');
3060                      }
3061                      if(php_sapi_name()!='cli') {
3062                          //We send to a browser
3063                          header('Content-Type: application/pdf');
3064                          if(headers_sent()) {
3065                              $this->Error('Some data has already been output to browser, can\'t send PDF file');
3066                          }
3067                          header('Content-Length: '.strlen($this->buffer));
3068                          header('Content-disposition: inline; filename="'.$name.'"');
3069                      }
3070                      echo $this->buffer;
3071                      break;
3072                  }
3073                  case 'D': {
3074                      //Download file
3075                      if(ob_get_contents()) {
3076                          $this->Error('Some data has already been output, can\'t send PDF file');
3077                      }
3078                      if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) {
3079                          header('Content-Type: application/force-download');
3080                      } else {
3081                          header('Content-Type: application/octet-stream');
3082                      }
3083                      if(headers_sent()) {
3084                          $this->Error('Some data has already been output to browser, can\'t send PDF file');
3085                      }
3086                      header('Content-Length: '.strlen($this->buffer));
3087                      header('Content-disposition: attachment; filename="'.$name.'"');
3088                      echo $this->buffer;
3089                      break;
3090                  }
3091                  case 'F': {
3092                      //Save to local file
3093                      $f=fopen($name,'wb');
3094                      if(!$f) {
3095                          $this->Error('Unable to create output file: '.$name);
3096                      }
3097                      fwrite($f,$this->buffer,strlen($this->buffer));
3098                      fclose($f);
3099                      break;
3100                  }
3101                  case 'S': {
3102                      //Return as a string
3103                      return $this->buffer;
3104                  }
3105                  default: {
3106                      $this->Error('Incorrect output destination: '.$dest);
3107                  }
3108              }
3109              return '';
3110          }
3111  
3112          // Protected methods
3113  
3114          /**
3115          * Check for locale-related bug
3116          * @access protected
3117          */
3118  		function _dochecks() {
3119              //Check for locale-related bug
3120              if(1.1==1) {
3121                  $this->Error('Don\'t alter the locale before including class file');
3122              }
3123              //Check for decimal separator
3124              if(sprintf('%.1f',1.0)!='1.0') {
3125                  setlocale(LC_NUMERIC,'C');
3126              }
3127          }
3128  
3129          /**
3130          * Return fonts path
3131          * @access protected
3132          */
3133  		function _getfontpath() {
3134              if(!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/font')) {
3135                  define('K_PATH_FONTS', dirname(__FILE__).'/font/');
3136              }
3137              return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
3138          }
3139  
3140          /**
3141          * Start document
3142          * @access protected
3143          */
3144  		function _begindoc() {
3145              //Start document
3146              $this->state=1;
3147              $this->_out('%PDF-'.$this->PDFVersion);
3148          }
3149  
3150          /**
3151          * _putpages
3152          * @access protected
3153          */
3154  		function _putpages() {
3155              $nb = $this->page;
3156              if(!empty($this->AliasNbPages)) {
3157                  $nbstr = $this->UTF8ToUTF16BE($nb, false);
3158                  //Replace number of pages
3159                  for($n=1;$n<=$nb;$n++) {
3160                      $this->pages[$n]=str_replace($this->AliasNbPages, $nbstr, $this->pages[$n]);
3161                  }
3162              }
3163              if($this->DefOrientation=='P') {
3164                  $wPt=$this->fwPt;
3165                  $hPt=$this->fhPt;
3166              }
3167              else {
3168                  $wPt=$this->fhPt;
3169                  $hPt=$this->fwPt;
3170              }
3171              $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
3172              for($n=1;$n<=$nb;$n++) {
3173                  //Page
3174                  $this->_newobj();
3175                  $this->_out('<</Type /Page');
3176                  $this->_out('/Parent 1 0 R');
3177                  if(isset($this->OrientationChanges[$n])) {
3178                      $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt));
3179                  }
3180                  $this->_out('/Resources 2 0 R');
3181                  if(isset($this->PageLinks[$n])) {
3182                      //Links
3183                      $annots='/Annots [';
3184                      foreach($this->PageLinks[$n] as $pl) {
3185                          $rect=sprintf('%.2f %.2f %.2f %.2f',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]);
3186                          $annots.='<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] ';
3187                          if(is_string($pl[4])) {
3188                              $annots.='/A <</S /URI /URI '.$this->_uristring($pl[4]).'>>>>';
3189                          }
3190                          else {
3191                              $l=$this->links[$pl[4]];
3192                              $h=isset($this->OrientationChanges[$l[0]]) ? $wPt : $hPt;
3193                              $annots.=sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>',1+2*$l[0],$h-$l[1]*$this->k);
3194                          }
3195                      }
3196                      $this->_out($annots.']');
3197                  }
3198                  $this->_out('/Contents '.($this->n+1).' 0 R>>');
3199                  $this->_out('endobj');
3200                  //Page content
3201                  $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
3202                  $this->_newobj();
3203                  $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
3204                  $this->_putstream($p);
3205                  $this->_out('endobj');
3206              }
3207              //Pages root
3208              $this->offsets[1]=strlen($this->buffer);
3209              $this->_out('1 0 obj');
3210              $this->_out('<</Type /Pages');
3211              $kids='/Kids [';
3212              for($i=0;$i<$nb;$i++) {
3213                  $kids.=(3+2*$i).' 0 R ';
3214              }
3215              $this->_out($kids.']');
3216              $this->_out('/Count '.$nb);
3217              $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt));
3218              $this->_out('>>');
3219              $this->_out('endobj');
3220          }
3221  
3222          /**
3223          * Adds fonts
3224          * _putfonts
3225          * @access protected
3226          */
3227  		function _putfonts() {
3228              $nf=$this->n;
3229              foreach($this->diffs as $diff) {
3230                  //Encodings
3231                  $this->_newobj();
3232                  $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
3233                  $this->_out('endobj');
3234              }
3235              $mqr=get_magic_quotes_runtime();
3236              set_magic_quotes_runtime(0);
3237              foreach($this->FontFiles as $file=>$info) {
3238                  //Font file embedding
3239                  $this->_newobj();
3240                  $this->FontFiles[$file]['n']=$this->n;
3241                  $font='';
3242                  $f=fopen($this->_getfontpath().strtolower($file),'rb',1);
3243                  if(!$f) {
3244                      $this->Error('Font file not found: '.$file);
3245                  }
3246                  while(!feof($f)) {
3247                      $font .= fread($f, 8192);
3248                  }
3249                  fclose($f);
3250                  $compressed=(substr($file,-2)=='.z');
3251                  if(!$compressed && isset($info['length2'])) {
3252                      $header=(ord($font{0})==128);
3253                      if($header) {
3254                          //Strip first binary header
3255                          $font=substr($font,6);
3256                      }
3257                      if($header && ord($font{$info['length1']})==128) {
3258                          //Strip second binary header
3259                          $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6);
3260                      }
3261                  }
3262                  $this->_out('<</Length '.strlen($font));
3263                  if($compressed) {
3264                      $this->_out('/Filter /FlateDecode');
3265                  }
3266                  $this->_out('/Length1 '.$info['length1']);
3267                  if(isset($info['length2'])) {
3268                      $this->_out('/Length2 '.$info['length2'].' /Length3 0');
3269                  }
3270                  $this->_out('>>');
3271                  $this->_putstream($font);
3272                  $this->_out('endobj');
3273              }
3274              set_magic_quotes_runtime($mqr);
3275              foreach($this->fonts as $k=>$font) {
3276                  //Font objects
3277                  $this->fonts[$k]['n']=$this->n+1;
3278                  $type=$font['type'];
3279                  $name=$font['name'];
3280                  if($type=='core') {
3281                      //Standard font
3282                      $this->_newobj();
3283                      $this->_out('<</Type /Font');
3284                      $this->_out('/BaseFont /'.$name);
3285                      $this->_out('/Subtype /Type1');
3286                      if($name!='Symbol' && $name!='ZapfDingbats') {
3287                          $this->_out('/Encoding /WinAnsiEncoding');
3288                      }
3289                      $this->_out('>>');
3290                      $this->_out('endobj');
3291                  } elseif($type=='Type1' OR $type=='TrueType') {
3292                      //Additional Type1 or TrueType font
3293                      $this->_newobj();
3294                      $this->_out('<</Type /Font');
3295                      $this->_out('/BaseFont /'.$name);
3296                      $this->_out('/Subtype /'.$type);
3297                      $this->_out('/FirstChar 32 /LastChar 255');
3298                      $this->_out('/Widths '.($this->n+1).' 0 R');
3299                      $this->_out('/FontDescriptor '.($this->n+2).' 0 R');
3300                      if($font['enc']) {
3301                          if(isset($font['diff'])) {
3302                              $this->_out('/Encoding '.($nf+$font['diff']).' 0 R');
3303                          } else {
3304                              $this->_out('/Encoding /WinAnsiEncoding');
3305                          }
3306                      }
3307                      $this->_out('>>');
3308                      $this->_out('endobj');
3309                      //Widths
3310                      $this->_newobj();
3311                      $cw=&$font['cw'];
3312                      $s='[';
3313                      for($i=32;$i<=255;$i++) {
3314                          $s.=$cw[chr($i)].' ';
3315                      }
3316                      $this->_out($s.']');
3317                      $this->_out('endobj');
3318                      //Descriptor
3319                      $this->_newobj();
3320                      $s='<</Type /FontDescriptor /FontName /'.$name;
3321                      foreach($font['desc'] as $k=>$v) {
3322                          $s.=' /'.$k.' '.$v;
3323                      }
3324                      $file = $font['file'];
3325                      if($file) {
3326                          $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
3327                      }
3328                      $this->_out($s.'>>');
3329                      $this->_out('endobj');
3330                  } else {
3331                      //Allow for additional types
3332                      $mtd='_put'.strtolower($type);
3333                      if(!method_exists($this, $mtd)) {
3334                          $this->Error('Unsupported font type: '.$type);
3335                      }
3336                      $this->$mtd($font);
3337                  }
3338              }
3339          }
3340  
3341          /**
3342          * _putimages
3343          * @access protected
3344          */
3345  		function _putimages() {
3346              $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
3347              reset($this->images);
3348              while(list($file,$info)=each($this->images)) {
3349                  $this->_newobj();
3350                  $this->images[$file]['n']=$this->n;
3351                  $this->_out('<</Type /XObject');
3352                  $this->_out('/Subtype /Image');
3353                  $this->_out('/Width '.$info['w']);
3354                  $this->_out('/Height '.$info['h']);
3355  
3356                  if (isset($info["masked"])) {
3357                      $this->_out('/SMask '.($this->n-1).' 0 R');
3358                  }
3359  
3360                  if($info['cs']=='Indexed') {
3361                      $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
3362                  }
3363                  else {
3364                      $this->_out('/ColorSpace /'.$info['cs']);
3365                      if($info['cs']=='DeviceCMYK') {
3366                          $this->_out('/Decode [1 0 1 0 1 0 1 0]');
3367                      }
3368                  }
3369                  $this->_out('/BitsPerComponent '.$info['bpc']);
3370                  if(isset($info['f'])) {
3371                      $this->_out('/Filter /'.$info['f']);
3372                  }
3373                  if(isset($info['parms'])) {
3374                      $this->_out($info['parms']);
3375                  }
3376                  if(isset($info['trns']) and is_array($info['trns'])) {
3377                      $trns='';
3378                      for($i=0;$i<count($info['trns']);$i++) {
3379                          $trns.=$info['trns'][$i].' '.$info['trns'][$i].' ';
3380                      }
3381                      $this->_out('/Mask ['.$trns.']');
3382                  }
3383                  $this->_out('/Length '.strlen($info['data']).'>>');
3384                  $this->_putstream($info['data']);
3385                  unset($this->images[$file]['data']);
3386                  $this->_out('endobj');
3387                  //Palette
3388                  if($info['cs']=='Indexed') {
3389                      $this->_newobj();
3390                      $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal'];
3391                      $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
3392                      $this->_putstream($pal);
3393                      $this->_out('endobj');
3394                  }
3395              }
3396          }
3397  
3398          /**
3399          * _putxobjectdict
3400          * @access protected
3401          */
3402  		function _putxobjectdict() {
3403              foreach($this->images as $image) {
3404                  $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
3405              }
3406          }
3407  
3408          /**
3409          * _putresourcedict
3410          * @access protected
3411          */
3412  		function _putresourcedict(){
3413              $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
3414              $this->_out('/Font <<');
3415              foreach($this->fonts as $font) {
3416                  $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
3417              }
3418              $this->_out('>>');
3419              $this->_out('/XObject <<');
3420              $this->_putxobjectdict();
3421              $this->_out('>>');
3422          }
3423  
3424          /**
3425          * _putresources
3426          * @access protected
3427          */
3428  		function _putresources() {
3429              $this->_putfonts();
3430              $this->_putimages();
3431              //Resource dictionary
3432              $this->offsets[2]=strlen($this->buffer);
3433              $this->_out('2 0 obj');
3434              $this->_out('<<');
3435              $this->_putresourcedict();
3436              $this->_out('>>');
3437              $this->_out('endobj');
3438              $this->_putjavascript();
3439              $this->_putbookmarks();
3440              // encryption
3441              if ($this->encrypted) {
3442                  $this->_newobj();
3443                  $this->enc_obj_id = $this->n;
3444                  $this->_out('<<');
3445                  $this->_putencryption();
3446                  $this->_out('>>');
3447                  $this->_out('endobj');
3448              }
3449          }
3450  
3451          /**
3452          * _putinfo
3453          * Adds some meta information
3454          * @access protected
3455          */
3456  		function _putinfo() {
3457              $this->_out('/CreationDate ('.$this->_escape('D:'.date('YmdHis')).')');
3458              $this->_out('/ModDate ('.$this->_escape('D:'.date('YmdHis')).')');
3459              $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
3460              if(!empty($this->title)) {
3461                  $this->_out('/Title '.$this->_textstring($this->title));
3462              }
3463              if(!empty($this->subject)) {
3464                  $this->_out('/Subject '.$this->_textstring($this->subject));
3465              }
3466              if(!empty($this->author)) {
3467                  $this->_out('/Author '.$this->_textstring($this->author));
3468              }
3469              if(!empty($this->keywords)) {
3470                  $this->_out('/Keywords '.$this->_textstring($this->keywords));
3471              }
3472              if(!empty($this->creator)) {
3473                  $this->_out('/Creator '.$this->_textstring($this->creator));
3474              }
3475          }
3476  
3477          /**
3478          * _putcatalog
3479          * @access protected
3480          */
3481  		function _putcatalog() {
3482              $this->_out('/Type /Catalog');
3483              $this->_out('/Pages 1 0 R');
3484              if($this->ZoomMode=='fullpage') {
3485                  $this->_out('/OpenAction [3 0 R /Fit]');
3486              }
3487              elseif($this->ZoomMode=='fullwidth') {
3488                  $this->_out('/OpenAction [3 0 R /FitH null]');
3489              }
3490              elseif($this->ZoomMode=='real') {
3491                  $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
3492              }
3493              elseif(!is_string($this->ZoomMode)) {
3494                  $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode/100).']');
3495              }
3496              if($this->LayoutMode=='single') {
3497                  $this->_out('/PageLayout /SinglePage');
3498              }
3499              elseif($this->LayoutMode=='continuous') {
3500                  $this->_out('/PageLayout /OneColumn');
3501              }
3502              elseif($this->LayoutMode=='two') {
3503                  $this->_out('/PageLayout /TwoColumnLeft');
3504              }
3505              if (!empty($this->javascript)) {
3506                  $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>');
3507              }
3508              if(count($this->outlines)>0) {
3509                  $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
3510                  $this->_out('/PageMode /UseOutlines');
3511              }
3512              if($this->rtl) {
3513                  $this->_out('/ViewerPreferences << /Direction /R2L >>');
3514              }
3515          }
3516  
3517          /**
3518          * _puttrailer
3519          * @access protected
3520          */
3521  		function _puttrailer() {
3522              $this->_out('/Size '.($this->n+1));
3523              $this->_out('/Root '.$this->n.' 0 R');
3524              $this->_out('/Info '.($this->n-1).' 0 R');
3525              if ($this->encrypted) {
3526                  $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
3527                  $this->_out('/ID [()()]');
3528              }
3529          }
3530  
3531          /**
3532          * _putheader
3533          * @access protected
3534          */
3535  		function _putheader() {
3536              $this->_out('%PDF-'.$this->PDFVersion);
3537          }
3538  
3539          /**
3540          * _enddoc
3541          * @access protected
3542          */
3543  		function _enddoc() {
3544              $this->_putheader();
3545              $this->_putpages();
3546              $this->_putresources();
3547              //Info
3548              $this->_newobj();
3549              $this->_out('<<');
3550              $this->_putinfo();
3551              $this->_out('>>');
3552              $this->_out('endobj');
3553              //Catalog
3554              $this->_newobj();
3555              $this->_out('<<');
3556              $this->_putcatalog();
3557              $this->_out('>>');
3558              $this->_out('endobj');
3559              //Cross-ref
3560              $o=strlen($this->buffer);
3561              $this->_out('xref');
3562              $this->_out('0 '.($this->n+1));
3563              $this->_out('0000000000 65535 f ');
3564              for($i=1;$i<=$this->n;$i++) {
3565                  $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
3566              }
3567              //Trailer
3568              $this->_out('trailer');
3569              $this->_out('<<');
3570              $this->_puttrailer();
3571              $this->_out('>>');
3572              $this->_out('startxref');
3573              $this->_out($o);
3574              $this->_out('%%EOF');
3575              $this->state=3;
3576          }
3577  
3578          /**
3579          * _beginpage
3580          * @access protected
3581          */
3582  		function _beginpage($orientation) {
3583              $this->page++;
3584              $this->pages[$this->page]='';
3585              $this->state=2;
3586              if ($this->rtl) {
3587                  $this->x = $this->w - $this->rMargin;
3588              } else {
3589                  $this->x = $this->lMargin;
3590              }
3591              $this->y = $this->tMargin;
3592              $this->FontFamily='';
3593              //Page orientation
3594              if(empty($orientation)) {
3595                  $orientation=$this->DefOrientation;
3596              }
3597              else {
3598                  $orientation=strtoupper($orientation{0});
3599                  if($orientation!=$this->DefOrientation) {
3600                      $this->OrientationChanges[$this->page]=true;
3601                  }
3602              }
3603              if($orientation!=$this->CurOrientation) {
3604                  //Change orientation
3605                  if($orientation=='P') {
3606                      $this->wPt=$this->fwPt;
3607                      $this->hPt=$this->fhPt;
3608                      $this->w=$this->fw;
3609                      $this->h=$this->fh;
3610                  }
3611                  else {
3612                      $this->wPt=$this->fhPt;
3613                      $this->hPt=$this->fwPt;
3614                      $this->w=$this->fh;
3615                      $this->h=$this->fw;
3616                  }
3617                  $this->PageBreakTrigger=$this->h-$this->bMargin;
3618                  $this->CurOrientation=$orientation;
3619              }
3620          }
3621  
3622          /**
3623          * End of page contents
3624          * @access protected
3625          */
3626  		function _endpage() {
3627              $this->state=1;
3628          }
3629  
3630          /**
3631          * Begin a new object
3632          * @access protected
3633          */
3634  		function _newobj() {
3635              $this->n++;
3636              $this->offsets[$this->n]=strlen($this->buffer);
3637              $this->_out($this->n.' 0 obj');
3638          }
3639  
3640          /**
3641          * Underline text
3642          * @param int $x X coordinate
3643          * @param int $y Y coordinate
3644          * @param string $txt text to underline
3645          * @access protected
3646          */
3647  		function _dounderline($x, $y, $txt) {
3648              $up = $this->CurrentFont['up'];
3649              $ut = $this->CurrentFont['ut'];
3650              $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt,' ');
3651              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);
3652          }
3653  
3654  
3655          // REWRITTEN by Warren Sherliker wsherliker@gmail.com
3656          // altered to allow compatibility with all sorts of image formats including gif.
3657          // Can easily extend to work with others
3658          // such as gd xbm etc. which are all supported by php 5+
3659          // (Requires GD library)
3660  
3661          /**
3662          * Extract info from a JPEG file
3663          * @param string $file image file to parse
3664          * @return string
3665          * @access protected
3666          */
3667  		function _parsejpg($file) {
3668              if(!function_exists('imagecreatefromjpeg')) {
3669                  // GD is not installed, try legacy method
3670                  return $this->_legacyparsejpg($file);
3671              }
3672              $a=getimagesize($file);
3673              if(empty($a)) {
3674                  $this->Error('Missing or incorrect image file: '.$file);
3675              }
3676              if($a[2]!=2) {
3677                  $this->Error('Not a JPEG file: '.$file);
3678              }
3679              $jpeg = imagecreatefromjpeg($file);
3680              return $this->outputjpg($file, $jpeg);
3681          }
3682  
3683          /**
3684          * Extract info from a GIF file
3685          * @param string $file image file to parse
3686          * @return string
3687          * @access protected
3688          */
3689  		function _parsegif($file) {
3690              if(!function_exists('imagecreatefromgif')) {
3691                  // PDF doesn't support native GIF and GD is not installed
3692                  return false;
3693              }
3694              $a=getimagesize($file);
3695              if(empty($a)) {
3696                  $this->Error('Missing or incorrect image file: '.$file);
3697              }
3698              if($a[2]!=1) {
3699                  $this->Error('Not a GIF file: '.$file);
3700              }
3701              // Temporary convert file to jpg and then delete this temp data file
3702              $gif = imagecreatefromgif($file);
3703              return $this->toJPEG($file, $gif);
3704          }
3705  
3706          /**
3707          * Extract info from a PNG file
3708          * @param string $file image file to parse
3709          * @return string
3710          * @access protected
3711          */
3712  		function _parsepng($file) {
3713              if(!function_exists('imagecreatefrompng')) {
3714                  // GD is not installed, try legacy method
3715                  return $this->_legacyparsepng($file);
3716              }
3717              $f=fopen($file,'rb');
3718              if(empty($f)) {
3719                  $this->Error('Can\'t open image file: '.$file);
3720              }
3721              //Check signature
3722              if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
3723                  $this->Error('Not a PNG file: '.$file);
3724              }
3725              //Read header chunk
3726              fread($f,4);
3727              if(fread($f,4)!='IHDR') {
3728                  $this->Error('Incorrect PNG file: '.$file);
3729              }
3730              // Temporary convert file to jpg and then delete this temp data file
3731              $a=getimagesize($file);
3732              $png = imagecreatefrompng($file);
3733              return $this->toJPEG($file, $png);
3734          }
3735  
3736          /**
3737          * Extract info from a JPEG file without using GD
3738          * @param string $file image file to parse
3739          * @return string
3740          * @access protected
3741          */
3742  		function _legacyparsejpg($file) {
3743              $a=GetImageSize($file);
3744              if(empty($a)) {
3745                  $this->Error('Missing or incorrect image file: '.$file);
3746              }
3747              if($a[2]!=2) {
3748                  $this->Error('Not a JPEG file: '.$file);
3749              }
3750              if(!isset($a['channels']) or $a['channels']==3) {
3751                  $colspace='DeviceRGB';
3752              }
3753              elseif($a['channels']==4) {
3754                  $colspace='DeviceCMYK';
3755              }
3756              else {
3757                  $colspace='DeviceGray';
3758              }
3759              $bpc=isset($a['bits']) ? $a['bits'] : 8;
3760              //Read whole file
3761              $f=fopen($file,'rb');
3762              $data='';
3763              while(!feof($f)) {
3764                  $data.=fread($f,4096);
3765              }
3766              fclose($f);
3767              return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data);
3768          }
3769  
3770          /**
3771          * Extract info from a PNG file without using GD
3772          * @param string $file image file to parse
3773          * @return string
3774          * @access protected
3775          */
3776  		function _legacyparsepng($file) {
3777              $f=fopen($file,'rb');
3778              if(empty($f)) {
3779                  $this->Error('Can\'t open image file: '.$file);
3780              }
3781              //Check signature
3782              if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
3783                  $this->Error('Not a PNG file: '.$file);
3784              }
3785              //Read header chunk
3786              fread($f,4);
3787              if(fread($f,4)!='IHDR') {
3788                  $this->Error('Incorrect PNG file: '.$file);
3789              }
3790              $w=$this->_freadint($f);
3791              $h=$this->_freadint($f);
3792              $bpc=ord(fread($f,1));
3793              if($bpc>8) {
3794                  $this->Error('16-bit depth not supported: '.$file);
3795              }
3796              $ct=ord(fread($f,1));
3797              if($ct==0) {
3798                  $colspace='DeviceGray';
3799              }
3800              elseif($ct==2) {
3801                  $colspace='DeviceRGB';
3802              }
3803              elseif($ct==3) {
3804                  $colspace='Indexed';
3805              }
3806              else {
3807                  $this->Error('Alpha channel not supported: '.$file);
3808              }
3809              if(ord(fread($f,1))!=0) {
3810                  $this->Error('Unknown compression method: '.$file);
3811              }
3812              if(ord(fread($f,1))!=0) {
3813                  $this->Error('Unknown filter method: '.$file);
3814              }
3815              if(ord(fread($f,1))!=0) {
3816                  $this->Error('Interlacing not supported: '.$file);
3817              }
3818              fread($f,4);
3819              $parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
3820              //Scan chunks looking for palette, transparency and image data
3821              $pal='';
3822              $trns='';
3823              $data='';
3824              do {
3825                  $n=$this->_freadint($f);
3826                  $type=fread($f,4);
3827                  if($type=='PLTE') {
3828                      //Read palette
3829                      $pal=fread($f,$n);
3830                      fread($f,4);
3831                  }
3832                  elseif($type=='tRNS') {
3833                      //Read transparency info
3834                      $t=fread($f,$n);
3835                      if($ct==0) {
3836                          $trns=array(ord(substr($t,1,1)));
3837                      }
3838                      elseif($ct==2) {
3839                          $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1)));
3840                      }
3841                      else {
3842                          $pos=strpos($t,chr(0));
3843                          if($pos!==false) {
3844                              $trns=array($pos);
3845                          }
3846                      }
3847                      fread($f,4);
3848                  }
3849                  elseif($type=='IDAT') {
3850                      //Read image data block
3851                      $data.=fread($f,$n);
3852                      fread($f,4);
3853                  }
3854                  elseif($type=='IEND') {
3855                      break;
3856                  }
3857                  else {
3858                      fread($f,$n+4);
3859                  }
3860              }
3861              while($n);
3862              if($colspace=='Indexed' and empty($pal)) {
3863                  $this->Error('Missing palette in '.$file);
3864              }
3865              fclose($f);
3866              return array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'parms'=>$parms, 'pal'=>$pal, 'trns'=>$trns, 'data'=>$data);
3867          }
3868  
3869          /**
3870          * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
3871          * @param string $file Image file name.
3872          * @param image $image Image object.
3873          * return image JPEG image object.
3874          * @access protected
3875          */
3876  		function toJPEG($file, $image) {
3877              if ($image) {
3878                  // output
3879                  $tempname = tempnam(K_PATH_CACHE,'jpg');
3880                  imagejpeg($image, $tempname, 100);
3881                  imagedestroy($image);
3882                  $retvars = $this->outputjpg($tempname);
3883                  // tidy up by removing temporary image
3884                  unlink($tempname);
3885                  return $retvars;
3886              } else {
3887                  $this->Error('Can\'t open image file: '.$file);
3888              }
3889          }
3890  
3891          /**
3892          * Get a JPEG filename and return a structure for the PDF creator.
3893          * @param string $filename JPEG file name.
3894          * @return array structure containing the image data
3895          * @access protected
3896          */
3897  		function outputjpg($filename) {
3898              $a=getimagesize($filename);
3899  
3900              if(!isset($a['channels']) or $a['channels']==3) {
3901                  $colspace='DeviceRGB';
3902              }
3903              elseif($a['channels']==4) {
3904                  $colspace='DeviceCMYK';
3905              }
3906              else {
3907                  $colspace='DeviceGray';
3908              }
3909              $bpc=isset($a['bits']) ? $a['bits'] : 8;
3910              //Read whole file
3911  
3912              $f=fopen($filename,'rb');
3913              $data='';
3914              while(!feof($f)) {
3915                  $data.=fread($f,4096);
3916              }
3917              fclose($f);
3918  
3919              return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data);
3920          }
3921  
3922          /// END OF REWRITE BY Warren Sherliker wsherliker@gmail.com
3923  
3924          /**
3925          * Read a 4-byte integer from file
3926          * @param string $f file name.
3927          * @return 4-byte integer
3928          * @access protected
3929          */
3930  		function _freadint($f) {
3931              $a=unpack('Ni',fread($f,4));
3932              return $a['i'];
3933          }
3934  
3935          /**
3936          * Format a text string for meta information
3937          * @param string $s string to escape.
3938          * @return string escaped string.
3939          * @access protected
3940          */
3941  		function _textstring($s) {
3942              if($this->isunicode) {
3943                  //Convert string to UTF-16BE
3944                  $s = $this->UTF8ToUTF16BE($s, true);
3945              }
3946              if ($this->encrypted) {
3947                  $s = $this->_RC4($this->_objectkey($this->n), $s);
3948              }
3949              return '('. $this->_escape($s).')';
3950          }
3951  
3952          /**
3953          * Format an URI string
3954          * @param string $s string to escape.
3955          * @return string escaped string.
3956          * @access protected
3957          */
3958  		function _uristring($s) {
3959              if ($this->encrypted) {
3960                  $s = $this->_RC4($this->_objectkey($this->n), $s);
3961              }
3962              return '('.$this->_escape($s).')';
3963          }
3964  
3965          /**
3966          * Format a text string
3967          * @param string $s string to escape.
3968          * @return string escaped string.
3969          * @access protected
3970          */
3971  		function _escapetext($s) {
3972              if($this->isunicode) {
3973                  //Convert string to UTF-16BE and reverse RTL language
3974                  $s = $this->utf8StrRev($s, false, $this->tmprtl);
3975              }
3976              return $this->_escape($s);
3977          }
3978  
3979          /**
3980          * Add \ before \, ( and )
3981          * @param string $s string to escape.
3982          * @return string escaped string.
3983          * @access protected
3984          */
3985  		function _escape($s) {
3986              // the chr(13) substitution fixes the Bugs item #1421290.
3987              return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
3988          }
3989  
3990          /**
3991          * Output a stream.
3992          * @param string $s string to output.
3993          * @access protected
3994          */
3995  		function _putstream($s) {
3996              if ($this->encrypted) {
3997                  $s = $this->_RC4($this->_objectkey($this->n), $s);
3998              }
3999              $this->_out('stream');
4000              $this->_out($s);
4001              $this->_out('endstream');
4002          }
4003  
4004          /**
4005          * Output a string to the document.
4006          * @param string $s string to output.
4007          * @access protected
4008          */
4009  		function _out($s) {
4010              if($this->state==2) {
4011                  $this->pages[$this->page] .= $s."\n";
4012              }
4013              else {
4014                  $this->buffer .= $s."\n";
4015              }
4016          }
4017  
4018          /**
4019          * Adds unicode fonts.<br>
4020          * Based on PDF Reference 1.3 (section 5)
4021          * @access protected
4022          * @author Nicola Asuni
4023          * @since 1.52.0.TC005 (2005-01-05)
4024          */
4025  		function _puttruetypeunicode($font) {
4026              // Type0 Font
4027              // A composite font composed of other fonts, organized hierarchically
4028              $this->_newobj();
4029              $this->_out('<</Type /Font');
4030              $this->_out('/Subtype /Type0');
4031              $this->_out('/BaseFont /'.$font['name'].'');
4032              $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.
4033              $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
4034              $this->_out('/ToUnicode '.($this->n + 2).' 0 R');
4035              $this->_out('>>');
4036              $this->_out('endobj');
4037  
4038              // CIDFontType2
4039              // A CIDFont whose glyph descriptions are based on TrueType font technology
4040              $this->_newobj();
4041              $this->_out('<</Type /Font');
4042              $this->_out('/Subtype /CIDFontType2');
4043              $this->_out('/BaseFont /'.$font['name'].'');
4044              $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R');
4045              $this->_out('/FontDescriptor '.($this->n + 3).' 0 R');
4046              if (isset($font['desc']['MissingWidth'])){
4047                  $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth
4048              }
4049              $w = "";
4050              foreach ($font['cw'] as $cid => $width) {
4051                  $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID
4052              }
4053              $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont
4054              $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R');
4055              $this->_out('>>');
4056              $this->_out('endobj');
4057  
4058              // ToUnicode
4059              // is a stream object that contains the definition of the CMap
4060              // (PDF Reference 1.3 chap. 5.9)
4061              $this->_newobj();
4062              $this->_out('<</Length 345>>');
4063              $this->_out('stream');
4064              $this->_out('/CIDInit /ProcSet findresource begin');
4065              $this->_out('12 dict begin');
4066              $this->_out('begincmap');
4067              $this->_out('/CIDSystemInfo');
4068              $this->_out('<</Registry (Adobe)');
4069              $this->_out('/Ordering (UCS)');
4070              $this->_out('/Supplement 0');
4071              $this->_out('>> def');
4072              $this->_out('/CMapName /Adobe-Identity-UCS def');
4073              $this->_out('/CMapType 2 def');
4074              $this->_out('1 begincodespacerange');
4075              $this->_out('<0000> <FFFF>');
4076              $this->_out('endcodespacerange');
4077              $this->_out('1 beginbfrange');
4078              $this->_out('<0000> <FFFF> <0000>');
4079              $this->_out('endbfrange');
4080              $this->_out('endcmap');
4081              $this->_out('CMapName currentdict /CMap defineresource pop');
4082              $this->_out('end');
4083              $this->_out('end');
4084              $this->_out('endstream');
4085              $this->_out('endobj');
4086  
4087              // CIDSystemInfo dictionary
4088              // A dictionary containing entries that define the character collection of the CIDFont.
4089              $this->_newobj();
4090              $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections
4091              $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry
4092              $this->_out('/Supplement 0'); // The supplement number of the character collection.
4093              $this->_out('>>');
4094              $this->_out('endobj');
4095  
4096              // Font descriptor
4097              // A font descriptor describing the CIDFont default metrics other than its glyph widths
4098              $this->_newobj();
4099              $this->_out('<</Type /FontDescriptor');
4100              $this->_out('/FontName /'.$font['name']);
4101              foreach ($font['desc'] as $key => $value) {
4102                  $this->_out('/'.$key.' '.$value);
4103              }
4104              if ($font['file']) {
4105                  // A stream containing a TrueType font program
4106                  $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
4107              }
4108              $this->_out('>>');
4109              $this->_out('endobj');
4110  
4111              // Embed CIDToGIDMap
4112              // A specification of the mapping from CIDs to glyph indices
4113              $this->_newobj();
4114              $ctgfile = $this->_getfontpath().strtolower($font['ctg']);
4115              if(!file_exists($ctgfile)) {
4116                  $this->Error('Font file not found: '.$ctgfile);
4117              }
4118              $size = filesize($ctgfile);
4119              $this->_out('<</Length '.$size.'');
4120              if(substr($ctgfile, -2) == '.z') { // check file extension
4121                  /* Decompresses data encoded using the public-domain
4122                  zlib/deflate compression method, reproducing the
4123                  original text or binary data */
4124                  $this->_out('/Filter /FlateDecode');
4125              }
4126              $this->_out('>>');
4127              $this->_putstream(file_get_contents($ctgfile));
4128              $this->_out('endobj');
4129          }
4130  
4131           /**
4132           * Converts UTF-8 strings to codepoints array.<br>
4133           * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
4134           * Based on: http://www.faqs.org/rfcs/rfc3629.html
4135           * <pre>
4136           *       Char. number range  |        UTF-8 octet sequence
4137           *       (hexadecimal)    |              (binary)
4138           *    --------------------+-----------------------------------------------
4139           *    0000 0000-0000 007F | 0xxxxxxx
4140           *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
4141           *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
4142           *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
4143           *    ---------------------------------------------------------------------
4144           *
4145           *   ABFN notation:
4146           *   ---------------------------------------------------------------------
4147           *   UTF8-octets = *( UTF8-char )
4148           *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
4149           *   UTF8-1      = %x00-7F
4150           *   UTF8-2      = %xC2-DF UTF8-tail
4151           *
4152           *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
4153           *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
4154           *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
4155           *                 %xF4 %x80-8F 2( UTF8-tail )
4156           *   UTF8-tail   = %x80-BF
4157           *   ---------------------------------------------------------------------
4158           * </pre>
4159           * @param string $str string to process.
4160           * @return array containing codepoints (UTF-8 characters values)
4161           * @access protected
4162           * @author Nicola Asuni
4163           * @since 1.53.0.TC005 (2005-01-05)
4164           */
4165  		function UTF8StringToArray($str) {
4166              if(!$this->isunicode) {
4167                  // split string into array of chars
4168                  $strarr = str_split($str);
4169                  // convert chars to equivalent code
4170                  while(list($pos,$char)=each($strarr)) {
4171                      $strarr[$pos] = ord($char);
4172                  }
4173                  return $strarr;
4174              }
4175              $unicode = array(); // array containing unicode values
4176              $bytes  = array(); // array containing single character byte sequences
4177              $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
4178  
4179              $str .= ""; // force $str to be a string
4180              $length = strlen($str);
4181  
4182              for($i = 0; $i < $length; $i++) {
4183                  $char = ord($str{$i}); // get one string character at time
4184                  if(count($bytes) == 0) { // get starting octect
4185                      if ($char <= 0x7F) {
4186                          $unicode[] = $char; // use the character "as is" because is ASCII
4187                          $numbytes = 1;
4188                      } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
4189                          $bytes[] = ($char - 0xC0) << 0x06;
4190                          $numbytes = 2;
4191                      } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
4192                          $bytes[] = ($char - 0xE0) << 0x0C;
4193                          $numbytes = 3;
4194                      } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
4195                          $bytes[] = ($char - 0xF0) << 0x12;
4196                          $numbytes = 4;
4197                      } else {
4198                          // use replacement character for other invalid sequences
4199                          $unicode[] = 0xFFFD;
4200                          $bytes = array();
4201                          $numbytes = 1;
4202                      }
4203                  } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
4204                      $bytes[] = $char - 0x80;
4205                      if (count($bytes) == $numbytes) {
4206                          // compose UTF-8 bytes to a single unicode value
4207                          $char = $bytes[0];
4208                          for($j = 1; $j < $numbytes; $j++) {
4209                              $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
4210                          }
4211                          if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
4212                              /* The definition of UTF-8 prohibits encoding character numbers between
4213                              U+D800 and U+DFFF, which are reserved for use with the UTF-16
4214                              encoding form (as surrogate pairs) and do not directly represent
4215                              characters. */
4216                              $unicode[] = 0xFFFD; // use replacement character
4217                          }
4218                          else {
4219                              $unicode[] = $char; // add char to array
4220                          }
4221                          // reset data for next char
4222                          $bytes = array();
4223                          $numbytes = 1;
4224                      }
4225                  } else {
4226                      // use replacement character for other invalid sequences
4227                      $unicode[] = 0xFFFD;
4228                      $bytes = array();
4229                      $numbytes = 1;
4230                  }
4231              }
4232              return $unicode;
4233          }
4234  
4235          /**
4236           * Converts UTF-8 strings to UTF16-BE.<br>
4237           * @param string $str string to process.
4238           * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
4239           * @return string
4240           * @access protected
4241           * @author Nicola Asuni
4242           * @since 1.53.0.TC005 (2005-01-05)
4243           * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
4244           */
4245  		function UTF8ToUTF16BE($str, $setbom=true) {
4246              if(!$this->isunicode) {
4247                  return $str; // string is not in unicode
4248              }
4249              $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
4250              return $this->arrUTF8ToUTF16BE($unicode, $setbom);
4251          }
4252  
4253          /**
4254           * Converts array of UTF-8 characters to UTF16-BE string.<br>
4255           * Based on: http://www.faqs.org/rfcs/rfc2781.html
4256            * <pre>
4257           *   Encoding UTF-16:
4258           *
4259            *   Encoding of a single character from an ISO 10646 character value to
4260           *    UTF-16 proceeds as follows. Let U be the character number, no greater
4261           *    than 0x10FFFF.
4262           *
4263           *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
4264           *       terminate.
4265           *
4266           *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
4267           *       U' must be less than or equal to 0xFFFFF. That is, U' can be
4268           *       represented in 20 bits.
4269           *
4270           *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
4271           *       0xDC00, respectively. These integers each have 10 bits free to
4272           *       encode the character value, for a total of 20 bits.
4273           *
4274           *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
4275           *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
4276           *       bits of W2. Terminate.
4277           *
4278           *    Graphically, steps 2 through 4 look like:
4279           *    U' = yyyyyyyyyyxxxxxxxxxx
4280           *    W1 = 110110yyyyyyyyyy
4281           *    W2 = 110111xxxxxxxxxx
4282           * </pre>
4283           * @param array $unicode array containing UTF-8 unicode values
4284           * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
4285           * @return string
4286           * @access protected
4287           * @author Nicola Asuni
4288           * @since 2.1.000 (2008-01-08)
4289           * @see UTF8ToUTF16BE()
4290           */
4291  		function arrUTF8ToUTF16BE($unicode, $setbom=true) {
4292              $outstr = ""; // string to be returned
4293              if ($setbom) {
4294                  $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
4295              }
4296              foreach($unicode as $char) {
4297                  if($char == 0xFFFD) {
4298                      $outstr .= "\xFF\xFD"; // replacement character
4299                  } elseif ($char < 0x10000) {
4300                      $outstr .= chr($char >> 0x08);
4301                      $outstr .= chr($char & 0xFF);
4302                  } else {
4303                      $char -= 0x10000;
4304                      $w1 = 0xD800 | ($char >> 0x10);
4305                      $w2 = 0xDC00 | ($char & 0x3FF);
4306                      $outstr .= chr($w1 >> 0x08);
4307                      $outstr .= chr($w1 & 0xFF);
4308                      $outstr .= chr($w2 >> 0x08);
4309                      $outstr .= chr($w2 & 0xFF);
4310                  }
4311              }
4312              return $outstr;
4313          }
4314          // ====================================================
4315  
4316          /**
4317            * Set header font.
4318           * @param array $font font
4319           * @since 1.1
4320           */
4321  		function setHeaderFont($font) {
4322              $this->header_font = $font;
4323          }
4324  
4325          /**
4326            * Set footer font.
4327           * @param array $font font
4328           * @since 1.1
4329           */
4330  		function setFooterFont($font) {
4331              $this->footer_font = $font;
4332          }
4333  
4334          /**
4335            * Set language array.
4336           * @param array $language
4337           * @since 1.1
4338           */
4339  		function setLanguageArray($language) {
4340              $this->l = $language;
4341              $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
4342          }
4343  
4344          /**
4345            * Set document barcode.
4346           * @param string $bc barcode
4347           */
4348  		function setBarcode($bc="") {
4349              $this->barcode = $bc;
4350          }
4351  
4352          /**
4353            * Print Barcode.
4354           * @param int $x x position in user units
4355           * @param int $y y position in user units
4356           * @param int $w width in user units
4357           * @param int $h height position in user units
4358           * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
4359           * @param string $style barcode style
4360           * @param string $font font for text
4361           * @param int $xres x resolution
4362           * @param string $code code to print
4363           */
4364  		function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
4365              require_once(dirname(__FILE__)."/barcode/barcode.php");
4366              require_once(dirname(__FILE__)."/barcode/i25object.php");
4367              require_once(dirname(__FILE__)."/barcode/c39object.php");
4368              require_once(dirname(__FILE__)."/barcode/c128aobject.php");
4369              require_once(dirname(__FILE__)."/barcode/c128bobject.php");
4370              require_once(dirname(__FILE__)."/barcode/c128cobject.php");
4371  
4372              if (empty($code)) {
4373                  return;
4374              }
4375  
4376              if (empty($style)) {
4377                  $style  = BCS_ALIGN_LEFT;
4378                  $style |= BCS_IMAGE_PNG;
4379                  $style |= BCS_TRANSPARENT;
4380                  //$style |= BCS_BORDER;
4381                  //$style |= BCS_DRAW_TEXT;
4382                  //$style |= BCS_STRETCH_TEXT;
4383                  //$style |= BCS_REVERSE_COLOR;
4384              }
4385              if (empty($font)) {$font = BCD_DEFAULT_FONT;}
4386              if (empty($xres)) {$xres = BCD_DEFAULT_XRES;}
4387  
4388              $scale_factor = 1.5 * $xres * $this->k;
4389              $bc_w = round($w * $scale_factor); //width in points
4390              $bc_h = round($h * $scale_factor); //height in points
4391  
4392              switch (strtoupper($type)) {
4393                  case "I25": {
4394                      $obj = new I25Object($bc_w, $bc_h, $style, $code);
4395                      break;
4396                  }
4397                  case "C128A": {
4398                      $obj = new C128AObject($bc_w, $bc_h, $style, $code);
4399                      break;
4400                  }
4401                  default:
4402                  case "C128B": {
4403                      $obj = new C128BObject($bc_w, $bc_h, $style, $code);
4404                      break;
4405                  }
4406                  case "C128C": {
4407                      $obj = new C128CObject($bc_w, $bc_h, $style, $code);
4408                      break;
4409                  }
4410                  case "C39": {
4411                      $obj = new C39Object($bc_w, $bc_h, $style, $code);
4412                      break;
4413                  }
4414              }
4415  
4416              $obj->SetFont($font);
4417              $obj->DrawObject($xres);
4418  
4419              //use a temporary file....
4420              $tmpName = tempnam(K_PATH_CACHE,'img');
4421              imagepng($obj->getImage(), $tmpName);
4422              $this->Image($tmpName, $x, $y, $w, $h, 'png');
4423              $obj->DestroyObject();
4424              unset($obj);
4425              unlink($tmpName);
4426          }
4427  
4428          /**
4429           * Returns the PDF data.
4430           */
4431  		function getPDFData() {
4432              if($this->state < 3) {
4433                  $this->Close();
4434              }
4435              return $this->buffer;
4436          }
4437  
4438          // --- HTML PARSER FUNCTIONS ---
4439  
4440          /**
4441           * Allows to preserve some HTML formatting.<br />
4442           * Supports: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small
4443           * @param string $html text to display
4444           * @param boolean $ln if true add a new line after text (default = true)
4445           * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
4446           * @param boolean $reseth if true reset the last cell height (default false).
4447           * @param boolean $cell if true add the default cMargin space to each Write (default false).
4448           */
4449  		function writeHTML($html, $ln=true, $fill=0, $reseth=false, $cell=false) {
4450  
4451              // store some variables
4452              $html=strip_tags($html,"<h1><h2><h3><h4><h5><h6><b><u><i><a><img><p><br><br/><strong><em><font><blockquote><li><ul><ol><hr><td><th><tr><table><sup><sub><small><span><div>"); //remove all unsupported tags
4453              //replace carriage returns, newlines and tabs
4454              $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
4455              $html = strtr($html, $repTable);
4456              $pattern = '/(<[^>]+>)/Uu';
4457              $a = preg_split($pattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //explodes the string
4458  
4459              if ((empty($this->lasth))OR ($reseth)) {
4460                  //set row height
4461                  $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO;
4462              }
4463  
4464              foreach($a as $key=>$element) {
4465                  if (!preg_match($pattern, $element)) {
4466                      //Text
4467                      if($this->HREF) {
4468                          $this->addHtmlLink($this->HREF, $element, $fill);
4469                      } elseif($this->tdbegin) {
4470                          if((strlen(trim($element)) > 0) AND ($element != "&nbsp;")) {
4471                              $this->Cell($this->tdwidth, $this->tdheight, $this->unhtmlentities($element), $this->tableborder, '', $this->tdalign, $this->tdbgcolor);
4472                          } elseif($element == "&nbsp;") {
4473                              $this->Cell($this->tdwidth, $this->tdheight, '', $this->tableborder, '', $this->tdalign, $this->tdbgcolor);
4474                          }
4475                      } else {
4476  
4477                          $ctmpmargin = $this->cMargin;
4478                          if(!$cell) {
4479                              $this->cMargin = 0;
4480                          }
4481  
4482                          $this->Write($this->lasth, stripslashes($this->unhtmlentities($element)), '', $fill, '', false, 0);
4483  
4484                          $this->cMargin = $ctmpmargin;
4485                      }
4486                  } else {
4487                      $element = substr($element, 1, -1);
4488                      //Tag
4489                      if($element{0}=='/') {
4490                          $this->closedHTMLTagHandler(strtolower(substr($element, 1)));
4491                      }
4492                      else {
4493                          //Extract attributes
4494                          // get tag name
4495                          preg_match('/([a-zA-Z0-9]*)/', $element, $tag);
4496                          $tag = strtolower($tag[0]);
4497                          // get attributes
4498                          preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER);
4499                          $attr = array(); // reset attribute array
4500                          while(list($id,$name)=each($attr_array[1])) {
4501                              $attr[strtolower($name)] = $attr_array[2][$id];
4502                          }
4503                          $this->openHTMLTagHandler($tag, $attr, $fill);
4504                      }
4505                  }
4506              }
4507              if ($ln) {
4508                  $this->Ln($this->lasth);
4509              }
4510          }
4511  
4512          /**
4513           * Prints a cell (rectangular area) with optional borders, background color and html text string. 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 />
4514           * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
4515           * @param float $w Cell width. If 0, the cell extends up to the right margin.
4516           * @param float $h Cell minimum height. The cell extends automatically if needed.
4517           * @param float $x upper-left corner X coordinate
4518           * @param float $y upper-left corner Y coordinate
4519           * @param string $html html text to print. Default value: empty string.
4520           * @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>
4521           * @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>
4522      Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
4523           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
4524           * @param boolean $reseth if true reset the last cell height (default true).
4525           * @see Cell()
4526           */
4527  		function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true) {
4528  
4529              if ((empty($this->lasth))OR ($reseth)) {
4530                  //set row height
4531                  $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO;
4532              }
4533  
4534              // get current page number
4535              $startpage = $this->page;
4536  
4537              if (!empty($y)) {
4538                  $this->SetY($y);
4539              } else {
4540                  $y = $this->GetY();
4541              }
4542              if (!empty($x)) {
4543                  $this->SetX($x);
4544              } else {
4545                  $x = $this->GetX();
4546              }
4547  
4548              if(empty($w)) {
4549                  if ($this->rtl) {
4550                      $w = $this->x - $this->lMargin;
4551                  } else {
4552                      $w = $this->w - $this->rMargin - $this->x;
4553                  }
4554              }
4555  
4556              // store original margin values
4557              $lMargin = $this->lMargin;
4558              $rMargin = $this->rMargin;
4559  
4560              // set new margin values
4561              if ($this->rtl) {
4562                  $this->SetLeftMargin($this->x - $w);
4563                  $this->SetRightMargin($this->w - $this->x);
4564              } else {
4565                  $this->SetLeftMargin($this->x);
4566                  $this->SetRightMargin($this->w - $this->x - $w);
4567              }
4568  
4569              // calculate remaining vertical space on first page ($startpage)
4570              $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin();
4571  
4572              // Write HTML text
4573              $this->writeHTML($html, true, $fill, $reseth, true);
4574  
4575              // Get end-of-text Y position
4576              $currentY = $this->GetY();
4577              // get latest page number
4578              $endpage = $this->page;
4579  
4580              if (!empty($border)) {
4581                  // check if a new page has been created
4582                  if ($endpage > $startpage) {
4583                      // design borders around HTML cells.
4584                      for ($page=$startpage; $page<=$endpage; $page++) {
4585                          $this->page = $page;
4586                          if ($page==$startpage) {
4587                              $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin());
4588                              $h = $restspace - 1;
4589                          } elseif ($page==$endpage) {
4590                              $this->SetY($this->tMargin); // put cursor at the beginning of text
4591                              $h = $currentY - $this->tMargin;
4592                          } else {
4593                              $this->SetY($this->tMargin); // put cursor at the beginning of text
4594                              $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
4595                          }
4596                          $this->SetX($x);
4597                          $this->Cell($w, $h, "", $border, 1, '', 0);
4598                      }
4599                  } else {
4600                      $h = max($h, ($currentY - $y));
4601                      $this->SetY($y); // put cursor at the beginning of text
4602                      $this->SetX($x);
4603                      // design a cell around the text
4604                      $this->Cell($w, $h, "", $border, 1, '', 0);
4605                  }
4606              }
4607  
4608              // restore original margin values
4609              $this->SetLeftMargin($lMargin);
4610              $this->SetRightMargin($rMargin);
4611  
4612              if($ln>0) {
4613                  //Go to the beginning of the next line
4614                  $this->SetY($currentY);
4615                  if($ln == 2) {
4616                      $this->SetX($x + $w);
4617                  }
4618              } else {
4619                  // go left or right by case
4620                  $this->page = $startpage;
4621                  $this->y = $y;
4622                  $this->SetX($x + $w);
4623              }
4624          }
4625  
4626          /**
4627           * Process opening tags.
4628           * @param string $tag tag name (in uppercase)
4629           * @param string $attr tag attribute (in uppercase)
4630           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
4631           * @access private
4632           */
4633  		function openHTMLTagHandler($tag, $attr, $fill=0) {
4634              // check for text direction attribute
4635              if (isset($attr['dir'])) {
4636                      $this->tmprtl = $attr['dir']=='rtl' ? 'R' : 'L';
4637              } else {
4638                  $this->tmprtl = false;
4639              }
4640              //Opening tag
4641              switch($tag) {
4642                  case 'table': {
4643                      if ((isset($attr['border'])) AND ($attr['border'] != '')) {
4644                          $this->tableborder = $attr['border'];
4645                      }
4646                      else {
4647                          $this->tableborder = 0;
4648                      }
4649                      break;
4650                  }
4651                  case 'tr': {
4652                      break;
4653                  }
4654                  case 'td':
4655                  case 'th': {
4656                      if ((isset($attr['width'])) AND ($attr['width'] != '')) {
4657                          $this->tdwidth = ($attr['width']/4);
4658                      }
4659                      else {
4660                          $this->tdwidth = (($this->w - $this->lMargin - $this->rMargin) / $this->default_table_columns);
4661                      }
4662                      if ((isset($attr['height'])) AND ($attr['height'] != '')) {
4663                          $this->tdheight=($attr['height'] / $this->k);
4664                      }
4665                      else {
4666                          $this->tdheight = $this->lasth;
4667                      }
4668                      if ((isset($attr['align'])) AND ($attr['align'] != '')) {
4669                          switch ($attr['align']) {
4670                              case 'center': {
4671                                  $this->tdalign = "C";
4672                                  break;
4673                              }
4674                              case 'right': {
4675                                  $this->tdalign = "R";
4676                                  break;
4677                              }
4678                              default:
4679                              case 'left': {
4680                                  $this->tdalign = "L";
4681                                  break;
4682                              }
4683                          }
4684                      } else {
4685                          if($this->rtl) {
4686                              $this->tdalign = "R";
4687                          } else {
4688                              $this->tdalign = "L";
4689                          }
4690                      }
4691                      if ((isset($attr['bgcolor'])) AND ($attr['bgcolor'] != '')) {
4692                          $coul = $this->convertColorHexToDec($attr['bgcolor']);
4693                          $this->SetFillColor($coul['R'], $coul['G'], $coul['B']);
4694                          $this->tdbgcolor=true;
4695                      }
4696                      $this->tdbegin=true;
4697                      break;
4698                  }
4699                  case 'hr': {
4700                      $this->Ln();
4701                      if ((isset($attr['width'])) AND ($attr['width'] != '')) {
4702                          $hrWidth = $attr['width'];
4703                      }
4704                      else {
4705                          $hrWidth = $this->w - $this->lMargin - $this->rMargin;
4706                      }
4707                      $x = $this->GetX();
4708                      $y = $this->GetY();
4709                      $this->GetLineWidth();
4710                      $prevlinewidth = $this->SetLineWidth(0.2);
4711                      $this->Line($x, $y, $x + $hrWidth, $y);
4712                      $this->SetLineWidth($prevlinewidth);
4713                      $this->Ln();
4714                      break;
4715                  }
4716                  case 'strong': {
4717                      $this->setStyle('b', true);
4718                      break;
4719                  }
4720                  case 'em': {
4721                      $this->setStyle('i', true);
4722                      break;
4723                  }
4724                  case 'b':
4725                  case 'i':
4726                  case 'u': {
4727                      $this->setStyle($tag, true);
4728                      break;
4729                  }
4730                  case 'a': {
4731                      $this->HREF = $attr['href'];
4732                      break;
4733                  }
4734                  case 'img': {
4735                      if(isset($attr['src'])) {
4736                          // replace relative path with real server path
4737                          if ($attr['src'][0] == '/') {
4738                              $attr['src'] = $_SERVER['DOCUMENT_ROOT'].$attr['src'];
4739                          }
4740                          $attr['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $attr['src']);
4741                          if(!isset($attr['width'])) {
4742                              $attr['width'] = 0;
4743                          }
4744                          if(!isset($attr['height'])) {
4745                              $attr['height'] = 0;
4746                          }
4747                          if(!isset($attr['align'])) {
4748                              $align = 'N';
4749                          } else {
4750                              switch($attr['align']) {
4751                                  case 'top':{
4752                                      $align = 'T';
4753                                      break;
4754                                  }
4755                                  case 'middle':{
4756                                      $align = 'M';
4757                                      break;
4758                                  }
4759                                  case 'bottom':{
4760                                      $align = 'B';
4761                                      break;
4762                                  }
4763                                  default:{
4764                                      $align = 'N';
4765                                      break;
4766                                  }
4767                              }
4768                          }
4769                          $this->Image($attr['src'], $this->GetX(),$this->GetY(), $this->pixelsToMillimeters($attr['width']), $this->pixelsToMillimeters($attr['height']), '', '', $align);
4770  
4771                      }
4772                      break;
4773                  }
4774                  case 'ul': {
4775                      $this->listordered = false;
4776                      $this->listcount = 0;
4777                      break;
4778                  }
4779                  case 'ol': {
4780                      $this->listordered = true;
4781                      $this->listcount = 0;
4782                      break;
4783                  }
4784                  case 'li': {
4785                      $this->Ln();
4786                      if ($this->listordered) {
4787                          if (isset($attr['value'])) {
4788                              $this->listcount = intval($attr['value']);
4789                          }
4790                          $this->lispacer = "    ".(++$this->listcount).". ";
4791                      } else {
4792                          //unordered list simbol
4793                          $this->lispacer = "    -  ";
4794                      }
4795                      $rtldir = $this->tmprtl;
4796                      $this->tmprtl = false;
4797                      $this->Write($this->lasth, $this->lispacer, '', $fill, '', false, 0);
4798                      $this->tmprtl = $rtldir;
4799                      break;
4800                  }
4801                  case 'blockquote':
4802                  case 'br': {
4803                      $this->Ln();
4804                      if(strlen($this->lispacer) > 0) {
4805                          if ($this->rtl) {
4806                              $this->x -= $this->GetStringWidth($this->lispacer);
4807                          } else {
4808                              $this->x += $this->GetStringWidth($this->lispacer);
4809                          }
4810                      }
4811                      break;
4812                  }
4813                  case 'p': {
4814                      $this->Ln();
4815                      $this->Ln();
4816                      break;
4817                  }
4818                  case 'sup': {
4819                      $currentFontSize = $this->FontSize;
4820                      $this->tempfontsize = $this->FontSizePt;
4821                      $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO);
4822                      $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO)));
4823                      break;
4824                  }
4825                  case 'sub': {
4826                      $currentFontSize = $this->FontSize;
4827                      $this->tempfontsize = $this->FontSizePt;
4828                      $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO);
4829                      $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO)));
4830                      break;
4831                  }
4832                  case 'small': {
4833                      $currentFontSize = $this->FontSize;
4834                      $this->tempfontsize = $this->FontSizePt;
4835                      $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO);
4836                      $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)/3));
4837                      break;
4838                  }
4839                  case 'font': {
4840                      if (isset($attr['color']) AND $attr['color']!='') {
4841                          $coul = $this->convertColorHexToDec($attr['color']);
4842                          $this->SetTextColor($coul['R'],$coul['G'],$coul['B']);
4843                          $this->issetcolor=true;
4844                      }
4845                      if (isset($attr['face']) and in_array(strtolower($attr['face']), $this->fontlist)) {
4846                          $this->SetFont(strtolower($attr['face']));
4847                          $this->issetfont=true;
4848                      }
4849                      if (isset($attr['size'])) {
4850                          $headsize = intval($attr['size']);
4851                      } else {
4852                          $headsize = 0;
4853                      }
4854                      $currentFontSize = $this->FontSize;
4855                      $this->tempfontsize = $this->FontSizePt;
4856                      $this->SetFontSize($this->FontSizePt + $headsize);
4857                      $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO;
4858                      break;
4859                  }
4860                  case 'h1':
4861                  case 'h2':
4862                  case 'h3':
4863                  case 'h4':
4864                  case 'h5':
4865                  case 'h6': {
4866                      $headsize = (4 - substr($tag, 1)) * 2;
4867                      $currentFontSize = $this->FontSize;
4868                      $this->tempfontsize = $this->FontSizePt;
4869                      $this->SetFontSize($this->FontSizePt + $headsize);
4870                      $this->setStyle('b', true);
4871                      $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO;
4872                      break;
4873                  }
4874              }
4875          }
4876  
4877          /**
4878           * Process closing tags.
4879           * @param string $tag tag name (in uppercase)
4880           * @access private
4881           */
4882  		function closedHTMLTagHandler($tag) {
4883              //Closing tag
4884              switch($tag) {
4885                  case 'td':
4886                  case 'th': {
4887                      $this->tdbegin = false;
4888                      $this->tdwidth = 0;
4889                      $this->tdheight = 0;
4890                      if($this->rtl) {
4891                          $this->tdalign = "R";
4892                      } else {
4893                          $this->tdalign = "L";
4894                      }
4895                      $this->tdbgcolor = false;
4896                      $this->SetFillColor($this->prevFillColor[0], $this->prevFillColor[1], $this->prevFillColor[2]);
4897                      break;
4898                  }
4899                  case 'tr': {
4900                      $this->Ln();
4901                      break;
4902                  }
4903                  case 'table': {
4904                      $this->tableborder=0;
4905                      break;
4906                  }
4907                  case 'strong': {
4908                      $this->setStyle('b', false);
4909                      break;
4910                  }
4911                  case 'em': {
4912                      $this->setStyle('i', false);
4913                      break;
4914                  }
4915                  case 'b':
4916                  case 'i':
4917                  case 'u': {
4918                      $this->setStyle($tag, false);
4919                      break;
4920                  }
4921                  case 'a': {
4922                      $this->HREF = '';
4923                      break;
4924                  }
4925                  case 'sup': {
4926                      $currentFontSize = $this->FontSize;
4927                      $this->SetFontSize($this->tempfontsize);
4928                      $this->tempfontsize = $this->FontSizePt;
4929                      $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO)));
4930                      break;
4931                  }
4932                  case 'sub': {
4933                      $currentFontSize = $this->FontSize;
4934                      $this->SetFontSize($this->tempfontsize);
4935                      $this->tempfontsize = $this->FontSizePt;
4936                      $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO)));
4937                      break;
4938                  }
4939                  case 'small': {
4940                      $currentFontSize = $this->FontSize;
4941                      $this->SetFontSize($this->tempfontsize);
4942                      $this->tempfontsize = $this->FontSizePt;
4943                      $this->SetXY($this->GetX(), $this->GetY() - (($this->FontSize - $currentFontSize)/3));
4944                      break;
4945                  }
4946                  case 'font': {
4947                      if ($this->issetcolor == true) {
4948                          $this->SetTextColor($this->prevTextColor[0], $this->prevTextColor[1], $this->prevTextColor[2]);
4949                      }
4950                      if ($this->issetfont) {
4951                          $this->FontFamily = $this->prevFontFamily;
4952                          $this->FontStyle = $this->prevFontStyle;
4953                          $this->SetFont($this->FontFamily);
4954                          $this->issetfont = false;
4955                      }
4956                      $currentFontSize = $this->FontSize;
4957                      $this->SetFontSize($this->tempfontsize);
4958                      $this->tempfontsize = $this->FontSizePt;
4959                      //$this->TextColor = $this->prevTextColor;
4960                      $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO;
4961                      break;
4962                  }
4963                  case 'p': {
4964                      $this->Ln();
4965                      $this->Ln();
4966                      break;
4967                  }
4968                  case 'ul':
4969                  case 'ol': {
4970                      $this->Ln();
4971                      $this->Ln();
4972                      break;
4973                  }
4974                  case 'li': {
4975                      $this->lispacer = "";
4976                      break;
4977                  }
4978                  case 'h1':
4979                  case 'h2':
4980                  case 'h3':
4981                  case 'h4':
4982                  case 'h5':
4983                  case 'h6': {
4984                      $currentFontSize = $this->FontSize;
4985                      $this->SetFontSize($this->tempfontsize);
4986                      $this->tempfontsize = $this->FontSizePt;
4987                      $this->setStyle('b', false);
4988                      $this->Ln();
4989                      $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO;
4990                      break;
4991                  }
4992                  default : {
4993                      break;
4994                  }
4995              }
4996              $this->tmprtl = false;
4997          }
4998  
4999          /**
5000           * Sets font style.
5001           * @param string $tag tag name (in lowercase)
5002           * @param boolean $enable
5003           * @access private
5004           */
5005  		function setStyle($tag, $enable) {
5006              //Modify style and select corresponding font
5007              $this->$tag += ($enable ? 1 : -1);
5008              $style='';
5009              foreach(array('b', 'i', 'u') as $s) {
5010                  if($this->$s > 0) {
5011                      $style .= $s;
5012                  }
5013              }
5014              $this->SetFont('', $style);
5015          }
5016  
5017          /**
5018           * Output anchor link.
5019           * @param string $url link URL
5020           * @param string $name link name
5021           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
5022           * @access public
5023           */
5024  		function addHtmlLink($url, $name, $fill=0) {
5025              //Put a hyperlink
5026              $this->SetTextColor(0, 0, 255);
5027              $this->setStyle('u', true);
5028              $this->Write($this->lasth, $name, $url, $fill, '', false, 0);
5029              $this->setStyle('u', false);
5030              $this->SetTextColor(0);
5031          }
5032  
5033          /**
5034           * Returns an associative array (keys: R,G,B) from
5035           * a hex html code (e.g. #3FE5AA).
5036           * @param string $color hexadecimal html color [#rrggbb]
5037           * @return array
5038           * @access private
5039           */
5040  		function convertColorHexToDec($color = "#000000"){
5041              $tbl_color = array();
5042              $tbl_color['R'] = hexdec(substr($color, 1, 2));
5043              $tbl_color['G'] = hexdec(substr($color, 3, 2));
5044              $tbl_color['B'] = hexdec(substr($color, 5, 2));
5045              return $tbl_color;
5046          }
5047  
5048          /**
5049           * Converts pixels to millimeters in 72 dpi.
5050           * @param int $px pixels
5051           * @return float millimeters
5052           * @access private
5053           */
5054  		function pixelsToMillimeters($px){
5055              return $px * 25.4 / 72;
5056          }
5057  
5058          /**
5059           * Reverse function for htmlentities.
5060           * Convert entities in UTF-8.
5061           *
5062           * @param $text_to_convert Text to convert.
5063           * @return string converted
5064           */
5065  		function unhtmlentities($text_to_convert) {
5066              if (!$this->isunicode) {
5067                  return html_entity_decode($text_to_convert);
5068              }
5069              require_once(dirname(__FILE__).'/html_entity_decode_php4.php');
5070              return html_entity_decode_php4($text_to_convert);
5071          }
5072  
5073          // ENCRYPTION METHODS ----------------------------------
5074          // SINCE 2.0.000 (2008-01-02)
5075          /**
5076          * Compute encryption key depending on object number where the encrypted data is stored
5077          * @param int $n object number
5078          * @since 2.0.000 (2008-01-02)
5079          */
5080  		function _objectkey($n) {
5081              return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10);
5082          }
5083  
5084          /**
5085           * Put encryption on PDF document
5086           * @since 2.0.000 (2008-01-02)
5087           */
5088  		function _putencryption() {
5089              $this->_out('/Filter /Standard');
5090              $this->_out('/V 1');
5091              $this->_out('/R 2');
5092              $this->_out('/O ('.$this->_escape($this->Ovalue).')');
5093              $this->_out('/U ('.$this->_escape($this->Uvalue).')');
5094              $this->_out('/P '.$this->Pvalue);
5095          }
5096  
5097          /**
5098          * Returns the input text exrypted using RC4 algorithm and the specified key.
5099          * RC4 is the standard encryption algorithm used in PDF format
5100          * @param string $key encryption key
5101          * @param String $text input text to be encrypted
5102          * @return String encrypted text
5103          * @since 2.0.000 (2008-01-02)
5104          * @author Klemen Vodopivec
5105          */
5106  		function _RC4($key, $text) {
5107              if ($this->last_rc4_key != $key) {
5108                  $k = str_repeat($key, 256/strlen($key)+1);
5109                  $rc4 = range(0,255);
5110                  $j = 0;
5111                  for ($i=0; $i<256; $i++) {
5112                      $t = $rc4[$i];
5113                      $j = ($j + $t + ord($k{$i})) % 256;
5114                      $rc4[$i] = $rc4[$j];
5115                      $rc4[$j] = $t;
5116                  }
5117                  $this->last_rc4_key = $key;
5118                  $this->last_rc4_key_c = $rc4;
5119              } else {
5120                  $rc4 = $this->last_rc4_key_c;
5121              }
5122              $len = strlen($text);
5123              $a = 0;
5124              $b = 0;
5125              $out = '';
5126              for ($i=0; $i<$len; $i++) {
5127                  $a = ($a+1)%256;
5128                  $t= $rc4[$a];
5129                  $b = ($b+$t)%256;
5130                  $rc4[$a] = $rc4[$b];
5131                  $rc4[$b] = $t;
5132                  $k = $rc4[($rc4[$a]+$rc4[$b])%256];
5133                  $out.=chr(ord($text{$i}) ^ $k);
5134              }
5135              return $out;
5136          }
5137  
5138          /**
5139          * Encrypts a string using MD5 and returns it's value as a binary string.
5140          * @param string $str input string
5141          * @return String MD5 encrypted binary string
5142          * @since 2.0.000 (2008-01-02)
5143          * @author Klemen Vodopivec
5144          */
5145  		function _md5_16($str) {
5146              return pack('H*',md5($str));
5147          }
5148  
5149          /**
5150          * Compute O value (used for RC4 encryption)
5151          * @param String $user_pass user password
5152          * @param String $owner_pass user password
5153          * @return String O value
5154          * @since 2.0.000 (2008-01-02)
5155          * @author Klemen Vodopivec
5156          */
5157  		function _Ovalue($user_pass, $owner_pass) {
5158              $tmp = $this->_md5_16($owner_pass);
5159              $owner_RC4_key = substr($tmp,0,5);
5160              return $this->_RC4($owner_RC4_key, $user_pass);
5161          }
5162  
5163          /**
5164          * Compute U value (used for RC4 encryption)
5165          * @return String U value
5166          * @since 2.0.000 (2008-01-02)
5167          * @author Klemen Vodopivec
5168          */
5169  		function _Uvalue() {
5170              return $this->_RC4($this->encryption_key, $this->padding);
5171          }
5172  
5173          /**
5174          * Compute encryption key
5175          * @param String $user_pass user password
5176          * @param String $owner_pass user password
5177          * @param String $protection protection type
5178          * @since 2.0.000 (2008-01-02)
5179          * @author Klemen Vodopivec
5180          */
5181  		function _generateencryptionkey($user_pass, $owner_pass, $protection) {
5182              // Pad passwords
5183              $user_pass = substr($user_pass.$this->padding,0,32);
5184              $owner_pass = substr($owner_pass.$this->padding,0,32);
5185              // Compute O value
5186              $this->Ovalue = $this->_Ovalue($user_pass,$owner_pass);
5187              // Compute encyption key
5188              $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
5189              $this->encryption_key = substr($tmp,0,5);
5190              // Compute U value
5191              $this->Uvalue = $this->_Uvalue();
5192              // Compute P value
5193              $this->Pvalue = -(($protection^255)+1);
5194          }
5195  
5196          /**
5197          * Set document protection
5198          * The permission array is composed of values taken from the following ones:
5199          * - copy: copy text and images to the clipboard
5200          * - print: print the document
5201          * - modify: modify it (except for annotations and forms)
5202          * - annot-forms: add annotations and forms
5203          * Remark: the protection against modification is for people who have the full Acrobat product.
5204          * 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.
5205          * 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.
5206          * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
5207          * @param String $user_pass user password. Empty by default.
5208          * @param String $owner_pass owner password. If not specified, a random value is used.
5209          * @since 2.0.000 (2008-01-02)
5210          * @author Klemen Vodopivec
5211          */
5212  		function SetProtection($permissions=array(),$user_pass='',$owner_pass=null) {
5213              $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
5214              $protection = 192;
5215              foreach($permissions as $permission) {
5216                  if (!isset($options[$permission])) {
5217                      $this->Error('Incorrect permission: '.$permission);
5218                  }
5219                  $protection += $options[$permission];
5220              }
5221              if ($owner_pass === null) {
5222                  $owner_pass = uniqid(rand());
5223              }
5224              $this->encrypted = true;
5225              $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
5226          }
5227  
5228          // END OF ENCRYPTION FUNCTIONS -------------------------
5229  
5230          // START TRANSFORMATIONS SECTION -----------------------
5231          // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni
5232  
5233          /**
5234          * Starts a 2D tranformation saving current graphic state.
5235          * This function must be called before scaling, mirroring, translation, rotation and skewing.
5236          * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5237          * @since 2.1.000 (2008-01-07)
5238          * @see StartTransform(), StopTransform()
5239          */
5240  		function StartTransform() {
5241              $this->_out('q');
5242          }
5243  
5244          /**
5245          * Stops a 2D tranformation restoring previous graphic state.
5246          * This function must be called after scaling, mirroring, translation, rotation and skewing.
5247          * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5248          * @since 2.1.000 (2008-01-07)
5249          * @see StartTransform(), StopTransform()
5250          */
5251  		function StopTransform() {
5252              $this->_out('Q');
5253          }
5254          /**
5255          * Horizontal Scaling.
5256          * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5257          * @param int $x abscissa of the scaling center. Default is current x position
5258          * @param int $y ordinate of the scaling center. Default is current y position
5259          * @since 2.1.000 (2008-01-07)
5260          * @see StartTransform(), StopTransform()
5261          */
5262  		function ScaleX($s_x, $x='', $y=''){
5263              $this->Scale($s_x, 100, $x, $y);
5264          }
5265  
5266          /**
5267          * Vertical Scaling.
5268          * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5269          * @param int $x abscissa of the scaling center. Default is current x position
5270          * @param int $y ordinate of the scaling center. Default is current y position
5271          * @since 2.1.000 (2008-01-07)
5272          * @see StartTransform(), StopTransform()
5273          */
5274  		function ScaleY($s_y, $x='', $y=''){
5275              $this->Scale(100, $s_y, $x, $y);
5276          }
5277  
5278          /**
5279          * Vertical and horizontal proportional Scaling.
5280          * @param float $s scaling factor for width and height as percent. 0 is not allowed.
5281          * @param int $x abscissa of the scaling center. Default is current x position
5282          * @param int $y ordinate of the scaling center. Default is current y position
5283          * @since 2.1.000 (2008-01-07)
5284          * @see StartTransform(), StopTransform()
5285          */
5286  		function ScaleXY($s, $x='', $y=''){
5287              $this->Scale($s, $s, $x, $y);
5288          }
5289  
5290          /**
5291          * Vertical and horizontal non-proportional Scaling.
5292          * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5293          * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5294          * @param int $x abscissa of the scaling center. Default is current x position
5295          * @param int $y ordinate of the scaling center. Default is current y position
5296          * @since 2.1.000 (2008-01-07)
5297          * @see StartTransform(), StopTransform()
5298          */
5299  		function Scale($s_x, $s_y, $x='', $y=''){
5300              if($x === '') {
5301                  $x=$this->x;
5302              }
5303              if($y === '') {
5304                  $y=$this->y;
5305              }
5306              if($this->rtl) {
5307                  $x = $this->w - $x;
5308              }
5309              if($s_x == 0 OR $s_y == 0)
5310                  $this->Error('Please use values unequal to zero for Scaling');
5311              $y=($this->h-$y)*$this->k;
5312              $x*=$this->k;
5313              //calculate elements of transformation matrix
5314              $s_x/=100;
5315              $s_y/=100;
5316              $tm[0]=$s_x;
5317              $tm[1]=0;
5318              $tm[2]=0;
5319              $tm[3]=$s_y;
5320              $tm[4]=$x*(1-$s_x);
5321              $tm[5]=$y*(1-$s_y);
5322              //scale the coordinate system
5323              $this->Transform($tm);
5324          }
5325  
5326          /**
5327          * Horizontal Mirroring.
5328          * @param int $x abscissa of the point. Default is current x position
5329          * @since 2.1.000 (2008-01-07)
5330          * @see StartTransform(), StopTransform()
5331          */
5332  		function MirrorH($x=''){
5333              $this->Scale(-100, 100, $x);
5334          }
5335  
5336          /**
5337          * Verical Mirroring.
5338          * @param int $y ordinate of the point. Default is current y position
5339          * @since 2.1.000 (2008-01-07)
5340          * @see StartTransform(), StopTransform()
5341          */
5342  		function MirrorV($y=''){
5343              $this->Scale(100, -100, '', $y);
5344          }
5345  
5346          /**
5347          * Point reflection mirroring.
5348          * @param int $x abscissa of the point. Default is current x position
5349          * @param int $y ordinate of the point. Default is current y position
5350          * @since 2.1.000 (2008-01-07)
5351          * @see StartTransform(), StopTransform()
5352          */
5353  		function MirrorP($x='',$y=''){
5354              $this->Scale(-100, -100, $x, $y);
5355          }
5356  
5357          /**
5358          * Reflection against a straight line through point (x, y) with the gradient angle (angle).
5359          * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
5360          * @param int $x abscissa of the point. Default is current x position
5361          * @param int $y ordinate of the point. Default is current y position
5362          * @since 2.1.000 (2008-01-07)
5363          * @see StartTransform(), StopTransform()
5364          */
5365  		function MirrorL($angle=0, $x='',$y=''){
5366              $this->Scale(-100, 100, $x, $y);
5367              $this->Rotate(-2*($angle-90),$x,$y);
5368          }
5369  
5370          /**
5371          * Translate graphic object horizontally.
5372          * @param int $t_x movement to the right
5373          * @since 2.1.000 (2008-01-07)
5374          * @see StartTransform(), StopTransform()
5375          */
5376  		function TranslateX($t_x){
5377              $this->Translate($t_x, 0);
5378          }
5379  
5380          /**
5381          * Translate graphic object vertically.
5382          * @param int $t_y movement to the bottom
5383          * @since 2.1.000 (2008-01-07)
5384          * @see StartTransform(), StopTransform()
5385          */
5386  		function TranslateY($t_y){
5387              $this->Translate(0, $t_y, $x, $y);
5388          }
5389  
5390          /**
5391          * Translate graphic object horizontally and vertically.
5392          * @param int $t_x movement to the right
5393          * @param int $t_y movement to the bottom
5394          * @since 2.1.000 (2008-01-07)
5395          * @see StartTransform(), StopTransform()
5396          */
5397  		function Translate($t_x, $t_y){
5398              if($this->rtl) {
5399                  $t_x = -$t_x;
5400              }
5401              //calculate elements of transformation matrix
5402              $tm[0]=1;
5403              $tm[1]=0;
5404              $tm[2]=0;
5405              $tm[3]=1;
5406              $tm[4]=$t_x*$this->k;
5407              $tm[5]=-$t_y*$this->k;
5408              //translate the coordinate system
5409              $this->Transform($tm);
5410          }
5411  
5412          /**
5413          * Rotate object.
5414          * @param float $angle angle in degrees for counter-clockwise rotation
5415          * @param int $x abscissa of the rotation center. Default is current x position
5416          * @param int $y ordinate of the rotation center. Default is current y position
5417          * @since 2.1.000 (2008-01-07)
5418          * @see StartTransform(), StopTransform()
5419          */
5420  		function Rotate($angle, $x='', $y=''){
5421              if($x === '') {
5422                  $x=$this->x;
5423              }
5424              if($y === '') {
5425                  $y=$this->y;
5426              }
5427              if($this->rtl) {
5428                  $x = $this->w - $x;
5429                  $angle = -$angle;
5430              }
5431              $y=($this->h-$y)*$this->k;
5432              $x*=$this->k;
5433              //calculate elements of transformation matrix
5434              $tm[0]=cos(deg2rad($angle));
5435              $tm[1]=sin(deg2rad($angle));
5436              $tm[2]=-$tm[1];
5437              $tm[3]=$tm[0];
5438              $tm[4]=$x+$tm[1]*$y-$tm[0]*$x;
5439              $tm[5]=$y-$tm[0]*$y-$tm[1]*$x;
5440              //rotate the coordinate system around ($x,$y)
5441              $this->Transform($tm);
5442          }
5443  
5444          /**
5445          * Skew horizontally.
5446          * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
5447          * @param int $x abscissa of the skewing center. default is current x position
5448          * @param int $y ordinate of the skewing center. default is current y position
5449          * @since 2.1.000 (2008-01-07)
5450          * @see StartTransform(), StopTransform()
5451          */
5452  		function SkewX($angle_x, $x='', $y=''){
5453              $this->Skew($angle_x, 0, $x, $y);
5454          }
5455  
5456          /**
5457          * Skew vertically.
5458          * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
5459          * @param int $x abscissa of the skewing center. default is current x position
5460          * @param int $y ordinate of the skewing center. default is current y position
5461          * @since 2.1.000 (2008-01-07)
5462          * @see StartTransform(), StopTransform()
5463          */
5464  		function SkewY($angle_y, $x='', $y=''){
5465              $this->Skew(0, $angle_y, $x, $y);
5466          }
5467  
5468          /**
5469          * Skew.
5470          * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
5471          * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
5472          * @param int $x abscissa of the skewing center. default is current x position
5473          * @param int $y ordinate of the skewing center. default is current y position
5474          * @since 2.1.000 (2008-01-07)
5475          * @see StartTransform(), StopTransform()
5476          */
5477  		function Skew($angle_x, $angle_y, $x='', $y=''){
5478              if($x === '') {
5479                  $x=$this->x;
5480              }
5481              if($y === '') {
5482                  $y=$this->y;
5483              }
5484              if($this->rtl) {
5485                  $x = $this->w - $x;
5486                  $angle_x = -$angle_x;
5487              }
5488              if($angle_x <= -90 OR $angle_x >= 90 OR $angle_y <= -90 OR $angle_y >= 90)
5489                  $this->Error('Please use values between -90� and 90� for skewing');
5490              $x*=$this->k;
5491              $y=($this->h-$y)*$this->k;
5492              //calculate elements of transformation matrix
5493              $tm[0]=1;
5494              $tm[1]=tan(deg2rad($angle_y));
5495              $tm[2]=tan(deg2rad($angle_x));
5496              $tm[3]=1;
5497              $tm[4]=-$tm[2]*$y;
5498              $tm[5]=-$tm[1]*$x;
5499              //skew the coordinate system
5500              $this->Transform($tm);
5501          }
5502  
5503          /**
5504          * Apply graphic transformations.
5505          * @since 2.1.000 (2008-01-07)
5506          * @see StartTransform(), StopTransform()
5507          */
5508  		function Transform($tm){
5509              $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0],$tm[1],$tm[2],$tm[3],$tm[4],$tm[5]));
5510          }
5511  
5512          // END TRANSFORMATIONS SECTION -------------------------
5513  
5514  
5515          // START GRAPHIC FUNCTIONS SECTION ---------------------
5516          // The following section is based on the code provided by David Hernandez Sanz
5517  
5518          /**
5519          * 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.
5520          * @param float $width The width.
5521          * @since 1.0
5522          * @see Line(), Rect(), Cell(), MultiCell()
5523          */
5524  		function SetLineWidth($width) {
5525              //Set line width
5526              $this->LineWidth=$width;
5527              if($this->page>0) {
5528                  $this->_out(sprintf('%.2f w',$width*$this->k));
5529              }
5530          }
5531  
5532          /**
5533          * Returns the current the line width.
5534          * @return int Line width
5535          * @since 2.1.000 (2008-01-07)
5536          * @see Line(), SetLineWidth()
5537          */
5538  		function GetLineWidth() {
5539              return $this->LineWidth;
5540          }
5541  
5542          /**
5543          * Set line style.
5544          *
5545          * @param array $style Line style. Array with keys among the following:
5546          * <ul>
5547          *     <li>width (float): Width of the line in user units.</li>
5548          *     <li>cap (string): Type of cap to put on the line. Possible values are:
5549          * butt, round, square. The difference between "square" and "butt" is that
5550          * "square" projects a flat end past the end of the line.</li>
5551          *     <li>join (string): Type of join. Possible values are: miter, round,
5552          * bevel.</li>
5553          *     <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
5554          * series of length values, which are the lengths of the on and off dashes.
5555          * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
5556          * 1 off, 2 on, 1 off, ...</li>
5557          *     <li>phase (integer): Modifier on the dash pattern which is used to shift
5558          * the point at which the pattern starts.</li>
5559          *     <li>color (array): Draw color. Format: array(red, green, blue).</li>
5560          * </ul>
5561          * @access public
5562          * @since 2.1.000 (2008-01-08)
5563          */
5564  		function SetLineStyle($style) {
5565              extract($style);
5566              if (isset($width)) {
5567                  $width_prev = $this->LineWidth;
5568                  $this->SetLineWidth($width);
5569                  $this->LineWidth = $width_prev;
5570              }
5571              if (isset($cap)) {
5572                  $ca = array("butt" => 0, "round"=> 1, "square" => 2);
5573                  if (isset($ca[$cap])) {
5574                      $this->_out($ca[$cap] . " J");
5575                  }
5576              }
5577              if (isset($join)) {
5578                  $ja = array("miter" => 0, "round" => 1, "bevel" => 2);
5579                  if (isset($ja[$join])) {
5580                      $this->_out($ja[$join] . " j");
5581                  }
5582              }
5583              if (isset($dash)) {
5584                  $dash_string = "";
5585                  if ($dash) {
5586                      if (ereg("^.+,", $dash)) {
5587                          $tab = explode(",", $dash);
5588                      } else {
5589                          $tab = array($dash);
5590                      }
5591                      $dash_string = "";
5592                      foreach ($tab as $i => $v) {
5593                          if ($i) {
5594                              $dash_string .= " ";
5595                          }
5596                          $dash_string .= sprintf("%.2f", $v);
5597                      }
5598                  }
5599                  if (!isset($phase) OR !$dash) {
5600                      $phase = 0;
5601                  }
5602                  $this->_out(sprintf("[%s] %.2f d", $dash_string, $phase));
5603              }
5604              if (isset($color)) {
5605                  list($r, $g, $b) = $color;
5606                  $this->SetDrawColor($r, $g, $b);
5607              }
5608          }
5609  
5610          /*
5611          * Set a draw point.
5612          * @param float $x Abscissa of point.
5613          * @param float $y Ordinate of point.
5614          * @access private
5615          * @since 2.1.000 (2008-01-08)
5616          */
5617  		function _outPoint($x, $y) {
5618              if($this->rtl) {
5619                  $x = $this->w - $x;
5620              }
5621              $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k));
5622          }
5623  
5624          /*
5625          * Draws a line from last draw point.
5626          * @param float $x Abscissa of end point.
5627          * @param float $y Ordinate of end point.
5628          * @access private
5629          * @since 2.1.000 (2008-01-08)
5630          */
5631  		function _outLine($x, $y) {
5632              if($this->rtl) {
5633                  $x = $this->w - $x;
5634              }
5635              $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k));
5636          }
5637  
5638          /**
5639          * Draws a rectangle.
5640          * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
5641          * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
5642          * @param float $w Width.
5643          * @param float $h Height.
5644          * @param string $op options
5645          * @access protected
5646          * @since 2.1.000 (2008-01-08)
5647          */
5648  		function _outRect($x, $y, $w, $h, $op) {
5649              if($this->rtl) {
5650                  $x = $this->w - $x - $w;
5651              }
5652              $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));
5653          }
5654  
5655          /*
5656          * Draws a Bezier curve from last draw point.
5657          * The Bezier curve is a tangent to the line between the control points at either end of the curve.
5658          * @param float $x1 Abscissa of control point 1.
5659          * @param float $y1 Ordinate of control point 1.
5660          * @param float $x2 Abscissa of control point 2.
5661          * @param float $y2 Ordinate of control point 2.
5662          * @param float $x3 Abscissa of end point.
5663          * @param float $y3 Ordinate of end point.
5664          * @access private
5665          * @since 2.1.000 (2008-01-08)
5666          */
5667  		function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
5668              if($this->rtl) {
5669                  $x1 = $this->w - $x1;
5670                  $x2 = $this->w - $x2;
5671                  $x3 = $this->w - $x3;
5672              }
5673              $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));
5674          }
5675  
5676          /**
5677          * Draws a line between two points.
5678          * @param float $x1 Abscissa of first point.
5679          * @param float $y1 Ordinate of first point.
5680          * @param float $x2 Abscissa of second point.
5681          * @param float $y2 Ordinate of second point.
5682          * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
5683          * @access public
5684          * @since 1.0
5685          * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
5686          */
5687  		function Line($x1, $y1, $x2, $y2, $style = array()) {
5688              if ($style) {
5689                  $this->SetLineStyle($style);
5690              }
5691              $this->_outPoint($x1, $y1);
5692              $this->_outLine($x2, $y2);
5693              $this->_out(" S");
5694          }
5695  
5696          /**
5697          * Draws a rectangle.
5698          * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
5699          * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
5700          * @param float $w Width.
5701          * @param float $h Height.
5702          * @param string $style Style of rendering. Possible values are:
5703          * <ul>
5704          *     <li>D or empty string: Draw (default).</li>
5705          *     <li>F: Fill.</li>
5706          *     <li>DF or FD: Draw and fill.</li>
5707          * </ul>
5708          * @param array $border_style Border style of rectangle. Array with keys among the following:
5709          * <ul>
5710          *     <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
5711          *     <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
5712          * </ul>
5713          * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
5714          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
5715          * @access public
5716          * @since 1.0
5717          * @see SetLineStyle()
5718          */
5719  		function Rect($x, $y, $w, $h, $style='', $border_style = array(), $fill_color = array()) {
5720              if (!(false === strpos($style, "F")) AND $fill_color) {
5721                  list($r, $g, $b) = $fill_color;
5722                  $this->SetFillColor($r, $g, $b);
5723              }
5724              switch ($style) {
5725                  case "F": {
5726                      $op='f';
5727                      $border_style = array();
5728                      $this->_outRect($x, $y, $w, $h, $op);
5729                      break;
5730                  }
5731                  case "DF":
5732                  case "FD": {
5733                      if (!$border_style OR isset($border_style["all"])) {
5734                          $op='B';
5735                          if (isset($border_style["all"])) {
5736                              $this->SetLineStyle($border_style["all"]);
5737                              $border_style = array();
5738                          }
5739                      } else {
5740                          $op='f';
5741                      }
5742                      $this->_outRect($x, $y, $w, $h, $op);
5743                      break;
5744                  }
5745                  default: {
5746                      $op='S';
5747                      if (!$border_style OR isset($border_style["all"])) {
5748                          if (isset($border_style["all"]) && $border_style["all"]) {
5749                              $this->SetLineStyle($border_style["all"]);
5750                              $border_style = array();
5751                          }
5752                          $this->_outRect($x, $y, $w, $h, $op);
5753                      }
5754                      break;
5755                  }
5756              }
5757              if ($border_style) {
5758                  $border_style2 = array();
5759                  foreach ($border_style as $line => $value) {
5760                      $lenght = strlen($line);
5761                      for ($i = 0; $i < $lenght; $i++) {
5762                          $border_style2[$line[$i]] = $value;
5763                      }
5764                  }
5765                  $border_style = $border_style2;
5766                  if (isset($border_style["L"]) && $border_style["L"]) {
5767                      $this->Line($x, $y, $x, $y + $h, $border_style["L"]);
5768                  }
5769                  if (isset($border_style["T"]) && $border_style["T"]) {
5770                      $this->Line($x, $y, $x + $w, $y, $border_style["T"]);
5771                  }
5772                  if (isset($border_style["R"]) && $border_style["R"]) {
5773                      $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]);
5774                  }
5775                  if (isset($border_style["B"]) && $border_style["B"]) {
5776                      $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]);
5777                  }
5778              }
5779          }
5780  
5781  
5782          /**
5783          * Draws a Bezier curve.
5784          * The Bezier curve is a tangent to the line between the control points at
5785          * either end of the curve.
5786          * @param float $x0 Abscissa of start point.
5787          * @param float $y0 Ordinate of start point.
5788          * @param float $x1 Abscissa of control point 1.
5789          * @param float $y1 Ordinate of control point 1.
5790          * @param float $x2 Abscissa of control point 2.
5791          * @param float $y2 Ordinate of control point 2.
5792          * @param float $x3 Abscissa of end point.
5793          * @param float $y3 Ordinate of end point.
5794          * @param string $style Style of rendering. Possible values are:
5795          * <ul>
5796          *     <li>D or empty string: Draw (default).</li>
5797          *     <li>F: Fill.</li>
5798          *     <li>DF or FD: Draw and fill.</li>
5799          * </ul>
5800          * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
5801          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
5802          * @access public
5803          * @see SetLineStyle()
5804          * @since 2.1.000 (2008-01-08)
5805          */
5806  		function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style = "", $line_style = array(), $fill_color = array()) {
5807              if (!(false === strpos($style, "F")) AND $fill_color) {
5808                  list($r, $g, $b) = $fill_color;
5809                  $this->SetFillColor($r, $g, $b);
5810              }
5811              switch ($style) {
5812                  case "F": {
5813                      $op = "f";
5814                      $line_style = array();
5815                      break;
5816                  }
5817                  case "FD":
5818                  case "DF": {
5819                      $op = "B";
5820                      break;
5821                  }
5822                  default: {
5823                      $op = "S";
5824                      break;
5825                  }
5826              }
5827              if ($line_style) {
5828                  $this->SetLineStyle($line_style);
5829              }
5830              $this->_outPoint($x0, $y0);
5831              $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
5832              $this->_out($op);
5833          }
5834  
5835          /**
5836          * Draws an ellipse.
5837          * An ellipse is formed from n Bezier curves.
5838          * @param float $x0 Abscissa of center point.
5839          * @param float $y0 Ordinate of center point.
5840          * @param float $rx Horizontal radius.
5841          * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
5842          * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
5843          * @param float $astart: Angle start of draw line. Default value: 0.
5844          * @param float $afinish: Angle finish of draw line. Default value: 360.
5845          * @param string $style Style of rendering. Possible values are:
5846          * <ul>
5847          *     <li>D or empty string: Draw (default).</li>
5848          *     <li>F: Fill.</li>
5849          *     <li>DF or FD: Draw and fill.</li>
5850          *     <li>C: Draw close.</li>
5851          * </ul>
5852          * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
5853          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
5854          * @param integer $nc Number of curves used in ellipse. Default value: 8.
5855          * @access public
5856          * @since 2.1.000 (2008-01-08)
5857          */
5858  		function Ellipse($x0, $y0, $rx, $ry = 0, $angle = 0, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) {
5859              if ($angle) {
5860                  $this->StartTransform();
5861                  $this->Rotate($angle, $x0, $y0);
5862                  $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
5863                  $this->StopTransform();
5864                  return;
5865              }
5866              if ($rx) {
5867                  if (!(false === strpos($style, "F")) AND $fill_color) {
5868                      list($r, $g, $b) = $fill_color;
5869                      $this->SetFillColor($r, $g, $b);
5870                  }
5871                  switch ($style) {
5872                      case "F": {
5873                          $op = "f";
5874                          $line_style = array();
5875                          break;
5876                      }
5877                      case "FD":
5878                      case "DF": {
5879                          $op = "B";
5880                          break;
5881                      }
5882                      case "C": {
5883                          $op = "s"; // Small "s" signifies closing the path as well
5884                          break;
5885                      }
5886                      default: {
5887                          $op = "S";
5888                          break;
5889                      }
5890                  }
5891                  if ($line_style) {
5892                      $this->SetLineStyle($line_style);
5893                  }
5894                  if (!$ry) {
5895                      $ry = $rx;
5896                  }
5897                  $rx *= $this->k;
5898                  $ry *= $this->k;
5899                  if ($nc < 2){
5900                      $nc = 2;
5901                  }
5902                  $astart = deg2rad((float) $astart);
5903                  $afinish = deg2rad((float) $afinish);
5904                  $total_angle = $afinish - $astart;
5905                  $dt = $total_angle / $nc;
5906                  $dtm = $dt/3;
5907                  $x0 *= $this->k;
5908                  $y0 = ($this->h - $y0) * $this->k;
5909                  $t1 = $astart;
5910                  $a0 = $x0 + ($rx * cos($t1));
5911                  $b0 = $y0 + ($ry * sin($t1));
5912                  $c0 = -$rx * sin($t1);
5913                  $d0 = $ry * cos($t1);
5914                  $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
5915                  for ($i = 1; $i <= $nc; $i++) {
5916                      // Draw this bit of the total curve
5917                      $t1 = ($i * $dt) + $astart;
5918                      $a1 = $x0 + ($rx * cos($t1));
5919                      $b1 = $y0 + ($ry * sin($t1));
5920                      $c1 = -$rx * sin($t1);
5921                      $d1 = $ry * cos($t1);
5922                      $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));
5923                      $a0 = $a1;
5924                      $b0 = $b1;
5925                      $c0 = $c1;
5926                      $d0 = $d1;
5927                  }
5928                  $this->_out($op);
5929              }
5930          }
5931  
5932          /**
5933          * Draws a circle.
5934          * A circle is formed from n Bezier curves.
5935          * @param float $x0 Abscissa of center point.
5936          * @param float $y0 Ordinate of center point.
5937          * @param float $r Radius.
5938          * @param float $astart: Angle start of draw line. Default value: 0.
5939          * @param float $afinish: Angle finish of draw line. Default value: 360.
5940          * @param string $style Style of rendering. Possible values are:
5941          * <ul>
5942          *     <li>D or empty string: Draw (default).</li>
5943          *     <li>F: Fill.</li>
5944          *     <li>DF or FD: Draw and fill.</li>
5945          *     <li>C: Draw close.</li>
5946          * </ul>
5947          * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
5948          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
5949          * @param integer $nc Number of curves used in circle. Default value: 8.
5950          * @access public
5951          * @since 2.1.000 (2008-01-08)
5952          */
5953  		function Circle($x0, $y0, $r, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) {
5954              $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
5955          }
5956  
5957          /**
5958          * Draws a polygon.
5959          * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
5960          * @param string $style Style of rendering. Possible values are:
5961          * <ul>
5962          *     <li>D or empty string: Draw (default).</li>
5963          *     <li>F: Fill.</li>
5964          *     <li>DF or FD: Draw and fill.</li>
5965          * </ul>
5966          * @param array $line_style Line style of polygon. Array with keys among the following:
5967          * <ul>
5968          *     <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
5969          *     <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
5970          * </ul>
5971          * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
5972          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
5973          * @access public
5974          * @since 2.1.000 (2008-01-08)
5975          */
5976  		function Polygon($p, $style = "", $line_style = array(), $fill_color = array()) {
5977              $np = count($p) / 2;
5978              if (!(false === strpos($style, "F")) AND $fill_color) {
5979                  list($r, $g, $b) = $fill_color;
5980                  $this->SetFillColor($r, $g, $b);
5981              }
5982              switch ($style) {
5983                  case "F": {
5984                      $line_style = array();
5985                      $op = "f";
5986                      break;
5987                  }
5988                  case "FD":
5989                  case "DF": {
5990                      $op = "B";
5991                      break;
5992                  }
5993                  default: {
5994                      $op = "S";
5995                      break;
5996                  }
5997              }
5998              $draw = true;
5999              if ($line_style) {
6000                  if (isset($line_style["all"])) {
6001                      $this->SetLineStyle($line_style["all"]);
6002                  }
6003                  else { // 0 .. (np - 1), op = {B, S}
6004                      $draw = false;
6005                      if ("B" == $op) {
6006                          $op = "f";
6007                          $this->_outPoint($p[0], $p[1]);
6008                          for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6009                              $this->_outLine($p[$i], $p[$i + 1]);
6010                          }
6011                          $this->_outLine($p[0], $p[1]);
6012                          $this->_out($op);
6013                      }
6014                      $p[$np * 2] = $p[0];
6015                      $p[($np * 2) + 1] = $p[1];
6016                      for ($i = 0; $i < $np; $i++) {
6017                          if (isset($line_style[$i])) {
6018                              $this->Line($p[$i * 2], $p[($i * 2) + 1], $p[($i * 2) + 2], $p[($i * 2) + 3], $line_style[$i]);
6019                          }
6020                      }
6021                  }
6022              }
6023              if ($draw) {
6024                  $this->_outPoint($p[0], $p[1]);
6025                  for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6026                      $this->_outLine($p[$i], $p[$i + 1]);
6027                  }
6028                  $this->_outLine($p[0], $p[1]);
6029                  $this->_out($op);
6030              }
6031          }
6032  
6033          /**
6034          * Draws a regular polygon.
6035          * @param float $x0 Abscissa of center point.
6036          * @param float $y0 Ordinate of center point.
6037          * @param float $r: Radius of inscribed circle.
6038          * @param integer $ns Number of sides.
6039          * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
6040          * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
6041          * @param string $style Style of rendering. Possible values are:
6042          * <ul>
6043          *     <li>D or empty string: Draw (default).</li>
6044          *     <li>F: Fill.</li>
6045          *     <li>DF or FD: Draw and fill.</li>
6046          * </ul>
6047          * @param array $line_style Line style of polygon sides. Array with keys among the following:
6048          * <ul>
6049          *     <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
6050          *     <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6051          * </ul>
6052          * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6053          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6054          * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6055          * <ul>
6056          *     <li>D or empty string: Draw (default).</li>
6057          *     <li>F: Fill.</li>
6058          *     <li>DF or FD: Draw and fill.</li>
6059          * </ul>
6060          * @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).
6061          * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6062          * @access public
6063          * @since 2.1.000 (2008-01-08)
6064          */
6065  		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()) {
6066              if (3 > $ns) {
6067                  $ns = 3;
6068              }
6069              if ($draw_circle) {
6070                  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6071              }
6072              $p = array();
6073              for ($i = 0; $i < $ns; $i++) {
6074                  $a = $angle + ($i * 360 / $ns);
6075                  $a_rad = deg2rad((float) $a);
6076                  $p[] = $x0 + ($r * sin($a_rad));
6077                  $p[] = $y0 + ($r * cos($a_rad));
6078              }
6079              $this->Polygon($p, $style, $line_style, $fill_color);
6080          }
6081  
6082          /**
6083          * Draws a star polygon
6084          * @param float $x0 Abscissa of center point.
6085          * @param float $y0 Ordinate of center point.
6086          * @param float $r Radius of inscribed circle.
6087          * @param integer $nv Number of vertices.
6088          * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
6089          * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6090          * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
6091          * @param string $style Style of rendering. Possible values are:
6092          * <ul>
6093          *     <li>D or empty string: Draw (default).</li>
6094          *     <li>F: Fill.</li>
6095          *     <li>DF or FD: Draw and fill.</li>
6096          * </ul>
6097          * @param array $line_style Line style of polygon sides. Array with keys among the following:
6098          * <ul>
6099          *     <li>all: Line style of all sides. Array like for
6100          * {@link SetLineStyle SetLineStyle}.</li>
6101          *     <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6102          * </ul>
6103          * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6104          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6105          * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6106          * <ul>
6107          *     <li>D or empty string: Draw (default).</li>
6108          *     <li>F: Fill.</li>
6109          *     <li>DF or FD: Draw and fill.</li>
6110          * </ul>
6111          * @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).
6112          * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6113          * @access public
6114          * @since 2.1.000 (2008-01-08)
6115          */
6116  		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()) {
6117              if (2 > $nv) {
6118                  $nv = 2;
6119              }
6120              if ($draw_circle) {
6121                  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6122              }
6123              $p2 = array();
6124              $visited = array();
6125              for ($i = 0; $i < $nv; $i++) {
6126                  $a = $angle + ($i * 360 / $nv);
6127                  $a_rad = deg2rad((float) $a);
6128                  $p2[] = $x0 + ($r * sin($a_rad));
6129                  $p2[] = $y0 + ($r * cos($a_rad));
6130                  $visited[] = false;
6131              }
6132              $p = array();
6133              $i = 0;
6134              do {
6135                  $p[] = $p2[$i * 2];
6136                  $p[] = $p2[($i * 2) + 1];
6137                  $visited[$i] = true;
6138                  $i += $ng;
6139                  $i %= $nv;
6140              } while (!$visited[$i]);
6141              $this->Polygon($p, $style, $line_style, $fill_color);
6142          }
6143  
6144          /**
6145          * Draws a rounded rectangle.
6146          * @param float $x Abscissa of upper-left corner.
6147          * @param float $y Ordinate of upper-left corner.
6148          * @param float $w Width.
6149          * @param float $h Height.
6150          * @param float $r Radius of the rounded corners.
6151          * @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").
6152          * @param string $style Style of rendering. Possible values are:
6153          * <ul>
6154          *     <li>D or empty string: Draw (default).</li>
6155          *     <li>F: Fill.</li>
6156          *     <li>DF or FD: Draw and fill.</li>
6157          * </ul>
6158          * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6159          * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6160          * @access public
6161          * @since 2.1.000 (2008-01-08)
6162          */
6163  		function RoundedRect($x, $y, $w, $h, $r, $round_corner = "1111", $style = "", $border_style = array(), $fill_color = array()) {
6164              if ("0000" == $round_corner) { // Not rounded
6165                  $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
6166              } else { // Rounded
6167                  if (!(false === strpos($style, "F")) AND $fill_color) {
6168                      list($red, $g, $b) = $fill_color;
6169                      $this->SetFillColor($red, $g, $b);
6170                  }
6171                  switch ($style) {
6172                      case "F": {
6173                          $border_style = array();
6174                          $op = "f";
6175                          break;
6176                      }
6177                      case "FD":
6178                      case "DF": {
6179                          $op = "B";
6180                          break;
6181                      }
6182                      default: {
6183                          $op = "S";
6184                          break;
6185                      }
6186                  }
6187                  if ($border_style) {
6188                      $this->SetLineStyle($border_style);
6189                  }
6190                  $MyArc = 4 / 3 * (sqrt(2) - 1);
6191                  $this->_outPoint($x + $r, $y);
6192                  $xc = $x + $w - $r;
6193                  $yc = $y + $r;
6194                  $this->_outLine($xc, $y);
6195                  if ($round_corner[0]) {
6196                      $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
6197                  } else {
6198                      $this->_outLine($x + $w, $y);
6199                  }
6200                  $xc = $x + $w - $r;
6201                  $yc = $y + $h - $r;
6202                  $this->_outLine($x + $w, $yc);
6203                  if ($round_corner[1]) {
6204                      $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
6205                  } else {
6206                      $this->_outLine($x + $w, $y + $h);
6207                  }
6208                  $xc = $x + $r;
6209                  $yc = $y + $h - $r;
6210                  $this->_outLine($xc, $y + $h);
6211                  if ($round_corner[2]) {
6212                      $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
6213                  } else {
6214                      $this->_outLine($x, $y + $h);
6215                  }
6216                  $xc = $x + $r;
6217                  $yc = $y + $r;
6218                  $this->_outLine($x, $yc);
6219                  if ($round_corner[3]) {
6220                      $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
6221                  } else {
6222                      $this->_outLine($x, $y);
6223                      $this->_outLine($x + $r, $y);
6224                  }
6225                  $this->_out($op);
6226              }
6227          }
6228  
6229          // END GRAPHIC FUNCTIONS SECTION -----------------------
6230  
6231          // BIDIRECTIONAL TEXT SECTION --------------------------
6232          /**
6233           * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
6234           * @param string $str string to manipulate.
6235           * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
6236           * @return string
6237           * @author Nicola Asuni
6238           * @since 2.1.000 (2008-01-08)
6239          */
6240  		function utf8StrRev($str, $setbom=false, $forcertl=false) {
6241              return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $forcertl=false), $setbom);
6242          }
6243  
6244          /**
6245           * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
6246           * @param array $ta array of characters composing the string.
6247           * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
6248           * @return string
6249           * @author Nicola Asuni
6250           * @since 2.4.000 (2008-03-06)
6251          */
6252  		function utf8Bidi($ta, $forcertl=false) {
6253              global $unicode,$unicode_mirror, $unicode_arlet;
6254              require_once(dirname(__FILE__).'/unicode_data.php');
6255  
6256              // paragraph embedding level
6257              $pel = 0;
6258              // max level
6259              $maxlevel = 0;
6260  
6261              // create string from array
6262              $str = $this->UTF8ArrSubString($ta);
6263  
6264              // check if string contains arabic text
6265              if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
6266                  $arabic = true;
6267              } else {
6268                  $arabic = false;
6269              }
6270  
6271              // check if string contains RTL text
6272              if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
6273                  return $ta;
6274              }
6275  
6276              // get number of chars
6277              $numchars = count($ta);
6278  
6279              if ($forcertl == 'R') {
6280                      $pel = 1;
6281              } elseif ($forcertl == 'L') {
6282                      $pel = 0;
6283              } else {
6284                  // P2. In each paragraph, find the first character of type L, AL, or R.
6285                  // 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.
6286                  for ($i=0; $i < $numchars; $i++) {
6287                      $type = $unicode[$ta[$i]];
6288                      if ($type == 'L') {
6289                          $pel = 0;
6290                          break;
6291                      } elseif (($type == 'AL') OR ($type == 'R')) {
6292                          $pel = 1;
6293                          break;
6294                      }
6295                  }
6296              }
6297  
6298              // Current Embedding Level
6299              $cel = $pel;
6300              // directional override status
6301              $dos = 'N';
6302              $remember = array();
6303              // start-of-level-run
6304              $sor = $pel % 2 ? 'R' : 'L';
6305              $eor = $sor;
6306  
6307              //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array()));
6308              //$current_level = &$levels[count( $levels )-1];
6309  
6310              // Array of characters data
6311              $chardata = Array();
6312  
6313              // 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.
6314              //     In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
6315              for ($i=0; $i < $numchars; $i++) {
6316                  if ($ta[$i] == K_RLE) {
6317                      // X2. With each RLE, compute the least greater odd embedding level.
6318                      //    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.
6319                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
6320                      $next_level = $cel + ($cel % 2) + 1;
6321                      if ($next_level < 62) {
6322                          $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
6323                          $cel = $next_level;
6324                          $dos = 'N';
6325                          $sor = $eor;
6326                          $eor = $cel % 2 ? 'R' : 'L';
6327                      }
6328                  } elseif ($ta[$i] == K_LRE) {
6329                      // X3. With each LRE, compute the least greater even embedding level.
6330                      //    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.
6331                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
6332                      $next_level = $cel + 2 - ($cel % 2);
6333                      if ( $next_level < 62 ) {
6334                          $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
6335                          $cel = $next_level;
6336                          $dos = 'N';
6337                          $sor = $eor;
6338                          $eor = $cel % 2 ? 'R' : 'L';
6339                      }
6340                  } elseif ($ta[$i] == K_RLO) {
6341                      // X4. With each RLO, compute the least greater odd embedding level.
6342                      //    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.
6343                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
6344                      $next_level = $cel + ($cel % 2) + 1;
6345                      if ($next_level < 62) {
6346                          $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
6347                          $cel = $next_level;
6348                          $dos = 'R';
6349                          $sor = $eor;
6350                          $eor = $cel % 2 ? 'R' : 'L';
6351                      }
6352                  } elseif ($ta[$i] == K_LRO) {
6353                      // X5. With each LRO, compute the least greater even embedding level.
6354                      //    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.
6355                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
6356                      $next_level = $cel + 2 - ($cel % 2);
6357                      if ( $next_level < 62 ) {
6358                          $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
6359                          $cel = $next_level;
6360                          $dos = 'L';
6361                          $sor = $eor;
6362                          $eor = $cel % 2 ? 'R' : 'L';
6363                      }
6364                  } elseif ($ta[$i] == K_PDF) {
6365                      // 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.
6366                      if (count($remember)) {
6367                          $last = count($remember ) - 1;
6368                          if (($remember[$last]['num'] == K_RLE) OR
6369                                ($remember[$last]['num'] == K_LRE) OR
6370                                ($remember[$last]['num'] == K_RLO) OR
6371                                ($remember[$last]['num'] == K_LRO)) {
6372                              $match = array_pop($remember);
6373                              $cel = $match['cel'];
6374                              $dos = $match['dos'];
6375                              $sor = $eor;
6376                              $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
6377                          }
6378                      }
6379                  } elseif (($ta[$i] != K_RLE) AND
6380                                   ($ta[$i] != K_LRE) AND
6381                                   ($ta[$i] != K_RLO) AND
6382                                   ($ta[$i] != K_LRO) AND
6383                                   ($ta[$i] != K_PDF)) {
6384                      // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
6385                      //    a. Set the level of the current character to the current embedding level.
6386                      //    b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
6387                      if ($dos != 'N') {
6388                          $chardir = $dos;
6389                      } else {
6390                          $chardir = $unicode[$ta[$i]];
6391                      }
6392                      // stores string characters and other information
6393                      $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
6394                  }
6395              } // end for each char
6396  
6397              // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
6398              // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
6399              // 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.
6400  
6401              // 3.3.3 Resolving Weak Types
6402              // 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.
6403              // Nonspacing marks are now resolved based on the previous characters.
6404              $numchars = count($chardata);
6405  
6406              // 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.
6407              $prevlevel = -1; // track level changes
6408              $levcount = 0; // counts consecutive chars at the same level
6409              for ($i=0; $i < $numchars; $i++) {
6410                  if ($chardata[$i]['type'] == 'NSM') {
6411                      if ($levcount) {
6412                          $chardata[$i]['type'] = $chardata[$i]['sor'];
6413                      } elseif ($i > 0) {
6414                          $chardata[$i]['type'] = $chardata[($i-1)]['type'];
6415                      }
6416                  }
6417                  if ($chardata[$i]['level'] != $prevlevel) {
6418                      $levcount = 0;
6419                  } else {
6420                      $levcount++;
6421                  }
6422                  $prevlevel = $chardata[$i]['level'];
6423              }
6424  
6425              // 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.
6426              $prevlevel = -1;
6427              $levcount = 0;
6428              for ($i=0; $i < $numchars; $i++) {
6429                  if ($chardata[$i]['char'] == 'EN') {
6430                      for ($j=$levcount; $j >= 0; $j--) {
6431                          if ($chardata[$j]['type'] == 'AL') {
6432                              $chardata[$i]['type'] = 'AN';
6433                          } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
6434                              break;
6435                          }
6436                      }
6437                  }
6438                  if ($chardata[$i]['level'] != $prevlevel) {
6439                      $levcount = 0;
6440                  } else {
6441                      $levcount++;
6442                  }
6443                  $prevlevel = $chardata[$i]['level'];
6444              }
6445  
6446              // W3. Change all ALs to R.
6447              for ($i=0; $i < $numchars; $i++) {
6448                  if ($chardata[$i]['type'] == 'AL') {
6449                      $chardata[$i]['type'] = 'R';
6450                  }
6451              }
6452  
6453              // 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.
6454              $prevlevel = -1;
6455              $levcount = 0;
6456              for ($i=0; $i < $numchars; $i++) {
6457                  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
6458                      if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
6459                          $chardata[$i]['type'] = 'EN';
6460                      } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
6461                          $chardata[$i]['type'] = 'EN';
6462                      } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
6463                          $chardata[$i]['type'] = 'AN';
6464                      }
6465                  }
6466                  if ($chardata[$i]['level'] != $prevlevel) {
6467                      $levcount = 0;
6468                  } else {
6469                      $levcount++;
6470                  }
6471                  $prevlevel = $chardata[$i]['level'];
6472              }
6473  
6474              // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
6475              $prevlevel = -1;
6476              $levcount = 0;
6477              for ($i=0; $i < $numchars; $i++) {
6478                  if($chardata[$i]['type'] == 'ET') {
6479                      if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
6480                          $chardata[$i]['type'] = 'EN';
6481                      } else {
6482                          $j = $i+1;
6483                          while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
6484                              if ($chardata[$j]['type'] == 'EN') {
6485                                  $chardata[$i]['type'] = 'EN';
6486                                  break;
6487                              } elseif ($chardata[$j]['type'] != 'ET') {
6488                                  break;
6489                              }
6490                              $j++;
6491                          }
6492                      }
6493                  }
6494                  if ($chardata[$i]['level'] != $prevlevel) {
6495                      $levcount = 0;
6496                  } else {
6497                      $levcount++;
6498                  }
6499                  $prevlevel = $chardata[$i]['level'];
6500              }
6501  
6502              // W6. Otherwise, separators and terminators change to Other Neutral.
6503              $prevlevel = -1;
6504              $levcount = 0;
6505              for ($i=0; $i < $numchars; $i++) {
6506                  if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
6507                      $chardata[$i]['type'] = 'ON';
6508                  }
6509                  if ($chardata[$i]['level'] != $prevlevel) {
6510                      $levcount = 0;
6511                  } else {
6512                      $levcount++;
6513                  }
6514                  $prevlevel = $chardata[$i]['level'];
6515              }
6516  
6517              //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.
6518              $prevlevel = -1;
6519              $levcount = 0;
6520              for ($i=0; $i < $numchars; $i++) {
6521                  if ($chardata[$i]['char'] == 'EN') {
6522                      for ($j=$levcount; $j >= 0; $j--) {
6523                          if ($chardata[$j]['type'] == 'L') {
6524                              $chardata[$i]['type'] = 'L';
6525                          } elseif ($chardata[$j]['type'] == 'R') {
6526                              break;
6527                          }
6528                      }
6529                  }
6530                  if ($chardata[$i]['level'] != $prevlevel) {
6531                      $levcount = 0;
6532                  } else {
6533                      $levcount++;
6534                  }
6535                  $prevlevel = $chardata[$i]['level'];
6536              }
6537  
6538              // 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.
6539              $prevlevel = -1;
6540              $levcount = 0;
6541              for ($i=0; $i < $numchars; $i++) {
6542                  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
6543                      if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
6544                          $chardata[$i]['type'] = 'L';
6545                      } elseif (($chardata[$i]['type'] == 'N') AND
6546                       (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
6547                       (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
6548                          $chardata[$i]['type'] = 'R';
6549                      } elseif ($chardata[$i]['type'] == 'N') {
6550                          // N2. Any remaining neutrals take the embedding direction
6551                          $chardata[$i]['type'] = $chardata[$i]['sor'];
6552                      }
6553                  } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
6554                      // first char
6555                      if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
6556                          $chardata[$i]['type'] = 'L';
6557                      } elseif (($chardata[$i]['type'] == 'N') AND
6558                       (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
6559                       (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
6560                          $chardata[$i]['type'] = 'R';
6561                      } elseif ($chardata[$i]['type'] == 'N') {
6562                          // N2. Any remaining neutrals take the embedding direction
6563                          $chardata[$i]['type'] = $chardata[$i]['sor'];
6564                      }
6565                  } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
6566                      //last char
6567                      if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
6568                          $chardata[$i]['type'] = 'L';
6569                      } elseif (($chardata[$i]['type'] == 'N') AND
6570                       (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
6571                       (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
6572                          $chardata[$i]['type'] = 'R';
6573                      } elseif ($chardata[$i]['type'] == 'N') {
6574                          // N2. Any remaining neutrals take the embedding direction
6575                          $chardata[$i]['type'] = $chardata[$i]['sor'];
6576                      }
6577                  } elseif ($chardata[$i]['type'] == 'N') {
6578                      // N2. Any remaining neutrals take the embedding direction
6579                      $chardata[$i]['type'] = $chardata[$i]['sor'];
6580                  }
6581                  if ($chardata[$i]['level'] != $prevlevel) {
6582                      $levcount = 0;
6583                  } else {
6584                      $levcount++;
6585                  }
6586                  $prevlevel = $chardata[$i]['level'];
6587              }
6588  
6589              // 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.
6590              // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
6591              for ($i=0; $i < $numchars; $i++) {
6592                  $odd = $chardata[$i]['level'] % 2;
6593                  if ($odd) {
6594                      if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
6595                          $chardata[$i]['level'] += 1;
6596                      }
6597                  } else {
6598                      if ($chardata[$i]['type'] == 'R') {
6599                          $chardata[$i]['level'] += 1;
6600                      } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
6601                          $chardata[$i]['level'] += 2;
6602                      }
6603                  }
6604                  $maxlevel = max($chardata[$i]['level'],$maxlevel);
6605              }
6606  
6607              // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
6608              //    1. Segment separators,
6609              //    2. Paragraph separators,
6610              //    3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
6611              //    4. Any sequence of white space characters at the end of the line.
6612              for ($i=0; $i < $numchars; $i++) {
6613                  if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
6614                      $chardata[$i]['level'] = $pel;
6615                  } elseif ($chardata[$i]['type'] == 'WS') {
6616                      $j = $i+1;
6617                      while ($j < $numchars) {
6618                          if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
6619                              (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
6620                              $chardata[$i]['level'] = $pel;;
6621                              break;
6622                          } elseif ($chardata[$j]['type'] != 'WS') {
6623                              break;
6624                          }
6625                          $j++;
6626                      }
6627                  }
6628              }
6629  
6630              // Arabic Shaping
6631              // 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.
6632              if ($arabic) {
6633                  for ($i=0; $i < $numchars; $i++) {
6634                      if ($unicode[$chardata[$i]['char']] == 'AL') {
6635                          if (($i > 0) AND (($i+1) < $numchars) AND
6636                                  ($unicode[$chardata[($i-1)]['char']] == 'AL') AND
6637                                  ($unicode[$chardata[($i+1)]['char']] == 'AL') AND
6638                                  ($chardata[($i-1)]['type'] == $chardata[$i]['type']) AND
6639                                  ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) {
6640                              // medial
6641                              if (isset($unicode_arlet[$chardata[$i]['char']][3])) {
6642                                  $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][3];
6643                              }
6644                          } elseif ((($i+1) < $numchars) AND
6645                                  ($unicode[$chardata[($i+1)]['char']] == 'AL') AND
6646                                  ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) {
6647                              // initial
6648                              if (isset($unicode_arlet[$chardata[$i]['char']][2])) {
6649                                  $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][2];
6650                              }
6651                          } elseif (($i > 0) AND
6652                                  ($unicode[$chardata[($i-1)]['char']] == 'AL') AND
6653                                  ($chardata[($i-1)]['type'] == $chardata[$i]['type'])) {
6654                              // final
6655                              if (isset($unicode_arlet[$chardata[$i]['char']][1])) {
6656                                  $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][1];
6657                              }
6658                          } elseif (isset($unicode_arlet[$chardata[$i]['char']][0])) {
6659                              // isolated
6660                              $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][0];
6661                          }
6662                      }
6663                  }
6664              }
6665  
6666              // 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.
6667              for ($j=$maxlevel; $j > 0; $j--) {
6668                  $ordarray = Array();
6669                  $revarr = Array();
6670                  $onlevel = false;
6671                  for ($i=0; $i < $numchars; $i++) {
6672                      if ($chardata[$i]['level'] >= $j) {
6673                          $onlevel = true;
6674                          if (isset($unicode_mirror[$chardata[$i]['char']])) {
6675                              // 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.
6676                              $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
6677                          }
6678                          $revarr[] = $chardata[$i];
6679                      } else {
6680                          if($onlevel) {
6681                              $revarr = array_reverse($revarr);
6682                              $ordarray = array_merge($ordarray, $revarr);
6683                              $revarr = Array();
6684                              $onlevel = false;
6685                          }
6686                          $ordarray[] = $chardata[$i];
6687                      }
6688                  }
6689                  if($onlevel) {
6690                      $revarr = array_reverse($revarr);
6691                      $ordarray = array_merge($ordarray, $revarr);
6692                  }
6693                  $chardata = $ordarray;
6694              }
6695  
6696              $ordarray = array();
6697              for ($i=0; $i < $numchars; $i++) {
6698                  $ordarray[] = $chardata[$i]['char'];
6699              }
6700  
6701              return $ordarray;
6702          }
6703  
6704          // END OF BIDIRECTIONAL TEXT SECTION -------------------
6705  
6706          /*
6707          * Adds a bookmark.
6708          * @param string $txt bookmark description.
6709          * @param int $level bookmark level.
6710          * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
6711          * @access public
6712          * @author Olivier Plathey, Nicola Asuni
6713          * @since 2.1.002 (2008-02-12)
6714          */
6715  		function Bookmark($txt, $level=0, $y=-1) {
6716          if($y == -1) {
6717                  $y = $this->GetY();
6718              }
6719              $this->outlines[]=array('t'=>$txt,'l'=>$level,'y'=>$y,'p'=>$this->PageNo());
6720          }
6721  
6722          /*
6723          * Create a bookmark PDF string.
6724          * @access private
6725          * @author Olivier Plathey, Nicola Asuni
6726          * @since 2.1.002 (2008-02-12)
6727          */
6728  		function _putbookmarks() {
6729              $nb = count($this->outlines);
6730              if($nb == 0) {
6731                  return;
6732              }
6733              $lru = array();
6734              $level = 0;
6735              foreach($this->outlines as $i=>$o) {
6736                  if($o['l'] > 0) {
6737                      $parent = $lru[$o['l'] - 1];
6738                      //Set parent and last pointers
6739                      $this->outlines[$i]['parent'] = $parent;
6740                      $this->outlines[$parent]['last'] = $i;
6741                      if($o['l'] > $level) {
6742                          //Level increasing: set first pointer
6743                          $this->outlines[$parent]['first'] = $i;
6744                      }
6745                  } else {
6746                      $this->outlines[$i]['parent']=$nb;
6747                  }
6748                  if($o['l']<=$level and $i>0) {
6749                      //Set prev and next pointers
6750                      $prev = $lru[$o['l']];
6751                      $this->outlines[$prev]['next'] = $i;
6752                      $this->outlines[$i]['prev'] = $prev;
6753                  }
6754                  $lru[$o['l']] = $i;
6755                  $level = $o['l'];
6756              }
6757              //Outline items
6758              $n = $this->n+1;
6759              foreach($this->outlines as $i=>$o) {
6760                  $this->_newobj();
6761                  $this->_out('<</Title '.$this->_textstring($o['t']));
6762                  $this->_out('/Parent '.($n+$o['parent']).' 0 R');
6763                  if(isset($o['prev']))
6764                  $this->_out('/Prev '.($n+$o['prev']).' 0 R');
6765                  if(isset($o['next']))
6766                  $this->_out('/Next '.($n+$o['next']).' 0 R');
6767                  if(isset($o['first']))
6768                  $this->_out('/First '.($n+$o['first']).' 0 R');
6769                  if(isset($o['last']))
6770                  $this->_out('/Last '.($n+$o['last']).' 0 R');
6771                  $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]',1+2*$o['p'],($this->h-$o['y'])*$this->k));
6772                  $this->_out('/Count 0>>');
6773                  $this->_out('endobj');
6774              }
6775              //Outline root
6776              $this->_newobj();
6777              $this->OutlineRoot=$this->n;
6778              $this->_out('<</Type /Outlines /First '.$n.' 0 R');
6779              $this->_out('/Last '.($n+$lru[0]).' 0 R>>');
6780              $this->_out('endobj');
6781          }
6782  
6783  
6784          // --- JAVASCRIPT - FORMS ------------------------------
6785  
6786          /*
6787          * Adds a javascript
6788          * @access public
6789          * @author Johannes G�ntert, Nicola Asuni
6790          * @since 2.1.002 (2008-02-12)
6791          */
6792  		function IncludeJS($script) {
6793              $this->javascript .= $script;
6794          }
6795  
6796          /*
6797          * Create a javascript PDF string.
6798          * @access private
6799          * @author Johannes G�ntert, Nicola Asuni
6800          * @since 2.1.002 (2008-02-12)
6801          */
6802  		function _putjavascript() {
6803              if (empty($this->javascript)) {
6804                  return;
6805              }
6806              $this->_newobj();
6807              $this->n_js = $this->n;
6808              $this->_out('<<');
6809              $this->_out('/Names [(EmbeddedJS) '.($this->n+1).' 0 R ]');
6810              $this->_out('>>');
6811              $this->_out('endobj');
6812              $this->_newobj();
6813              $this->_out('<<');
6814              $this->_out('/S /JavaScript');
6815              $this->_out('/JS '.$this->_textstring($this->javascript));
6816              $this->_out('>>');
6817              $this->_out('endobj');
6818          }
6819  
6820          /*
6821          * Convert color to javascript color.
6822          * @param string $color color name or #RRGGBB
6823          * @access private
6824          * @author Denis Van Nuffelen, Nicola Asuni
6825          * @since 2.1.002 (2008-02-12)
6826          */
6827  		function _JScolor($color) {
6828              static $aColors = array('transparent','black','white','red','green','blue','cyan','magenta','yellow','dkGray','gray','ltGray');
6829              if(substr($color,0,1) == '#') {
6830                  return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
6831              }
6832              if(!in_array($color,$aColors)) {
6833                  $this->Error('Invalid color: '.$color);
6834              }
6835              return 'color.'.$color;
6836          }
6837  
6838          /*
6839          * Adds a javascript form field.
6840          * @param string $type field type
6841          * @param string $name field name
6842          * @param int $x horizontal position
6843          * @param int $y vertical position
6844          * @param int $w width
6845          * @param int $h height
6846          * @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>
6847          * @access private
6848          * @author Denis Van Nuffelen, Nicola Asuni
6849          * @since 2.1.002 (2008-02-12)
6850          */
6851  		function _addfield($type, $name, $x, $y, $w, $h, $prop) {
6852              $k = $this->k;
6853              $this->javascript .= sprintf("f=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);
6854              $this->javascript .= 'f.textSize='.$this->FontSizePt.';';
6855              while(list($key, $val) = each($prop)) {
6856                  if (strcmp(substr($key,-5),"Color") == 0) {
6857                      $val = $this->_JScolor($val);
6858                  } else {
6859                      $val = "'".$val."'";
6860                  }
6861                  $this->javascript .= "f.".$key."=".$val.";";
6862              }
6863              $this->x+=$w;
6864          }
6865  
6866          /*
6867          * Creates a text field
6868          * @param string $name field name
6869          * @param int $w width
6870          * @param int $h height
6871          * @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>
6872          * @access public
6873          * @author Denis Van Nuffelen, Nicola Asuni
6874          * @since 2.1.002 (2008-02-12)
6875          */
6876  		function TextField($name, $w, $h, $prop=array()) {
6877              $this->_addfield('text',$name,$this->x,$this->y,$w,$h,$prop);
6878          }
6879  
6880          /*
6881          * Creates a RadioButton field
6882          * @param string $name field name
6883          * @param int $w width
6884          * @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>
6885          * @access public
6886          * @author Nicola Asuni
6887          * @since 2.2.003 (2008-03-03)
6888          */
6889  		function RadioButton($name, $w, $prop=array()) {
6890              if(!isset($prop['strokeColor'])) {
6891                  $prop['strokeColor']='black';
6892              }
6893              $this->_addfield('radiobutton',$name,$this->x,$this->y,$w,$w,$prop);
6894          }
6895  
6896          /*
6897          * Creates a List-box field
6898          * @param string $name field name
6899          * @param int $w width
6900          * @param int $h height
6901          * @param array $values array containing the list of values.
6902          * @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>
6903          * @access public
6904          * @author Nicola Asuni
6905          * @since 2.2.003 (2008-03-03)
6906          */
6907  		function ListBox($name, $w, $h, $values, $prop=array()) {
6908              if(!isset($prop['strokeColor'])) {
6909                  $prop['strokeColor']='ltGray';
6910              }
6911              $this->_addfield('listbox',$name,$this->x,$this->y,$w,$h,$prop);
6912              $s = '';
6913              foreach($values as $value) {
6914                  $s .= "'".addslashes($value)."',";
6915              }
6916              $this->javascript .= 'f.setItems(['.substr($s,0,-1).']);';
6917          }
6918  
6919          /*
6920          * Creates a Combo-box field
6921          * @param string $name field name
6922          * @param int $w width
6923          * @param int $h height
6924          * @param array $values array containing the list of values.
6925          * @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>
6926          * @access public
6927          * @author Denis Van Nuffelen, Nicola Asuni
6928          * @since 2.1.002 (2008-02-12)
6929          */
6930  		function ComboBox($name, $w, $h, $values, $prop=array()) {
6931              $this->_addfield('combobox',$name,$this->x,$this->y,$w,$h,$prop);
6932              $s = '';
6933              foreach($values as $value) {
6934                  $s .= "'".addslashes($value)."',";
6935              }
6936              $this->javascript .= 'f.setItems(['.substr($s,0,-1).']);';
6937          }
6938  
6939          /*
6940          * Creates a CheckBox field
6941          * @param string $name field name
6942          * @param int $w width
6943          * @param boolean $checked define the initial state (default = false).
6944          * @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>
6945          * @access public
6946          * @author Denis Van Nuffelen, Nicola Asuni
6947          * @since 2.1.002 (2008-02-12)
6948          */
6949  		function CheckBox($name, $w, $checked=false, $prop=array()) {
6950              $prop['value'] = ($checked ? 'Yes' : 'Off');
6951              if(!isset($prop['strokeColor'])) {
6952                  $prop['strokeColor']='black';
6953              }
6954              $this->_addfield('checkbox',$name,$this->x,$this->y,$w,$w,$prop);
6955          }
6956  
6957          /*
6958          * Creates a button field
6959          * @param string $name field name
6960          * @param int $w width
6961          * @param int $h height
6962          * @param string $caption caption.
6963          * @param string $action action triggered by the button (JavaScript code).
6964          * @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>
6965          * @access public
6966          * @author Denis Van Nuffelen, Nicola Asuni
6967          * @since 2.1.002 (2008-02-12)
6968          */
6969  		function Button($name, $w, $h, $caption, $action, $prop=array()) {
6970              if(!isset($prop['strokeColor'])) {
6971                  $prop['strokeColor']='black';
6972              }
6973              if(!isset($prop['borderStyle'])) {
6974                  $prop['borderStyle']='beveled';
6975              }
6976              $this->_addfield('button',$name,$this->x,$this->y,$w,$h,$prop);
6977              $this->javascript .= "f.buttonSetCaption('".addslashes($caption)."');";
6978              $this->javascript .= "f.setAction('MouseUp','".addslashes($action)."');";
6979              $this->javascript .= "f.highlight='push';";
6980              $this->javascript .= 'f.print=false;';
6981          }
6982  
6983          // END JAVASCRIPT - FORMS ------------------------------
6984  
6985      } // END OF TCPDF CLASS
6986  
6987      //Handle special IE contype request
6988      if(isset($_SERVER['HTTP_USER_AGENT']) AND ($_SERVER['HTTP_USER_AGENT']=='contype')) {
6989          header('Content-Type: application/pdf');
6990          exit;
6991      }
6992  
6993  }
6994  //============================================================+
6995  // END OF FILE
6996  //============================================================+
6997  ?>


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1