tcpdf.php

Go to the documentation of this file.
00001 <?php
00002 //============================================================+
00003 // File name   : tcpdf.php
00004 // Begin       : 2002-08-03
00005 // Last Update : 2009-09-23
00006 // Author      : Nicola Asuni - [email protected] - http://www.tcpdf.org
00007 // Version     : 4.8.007
00008 // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
00009 //  ----------------------------------------------------------------------------
00010 //  Copyright (C) 2002-2009  Nicola Asuni - Tecnick.com S.r.l.
00011 //  
00012 //  This program is free software: you can redistribute it and/or modify
00013 //  it under the terms of the GNU Lesser General Public License as published by
00014 //  the Free Software Foundation, either version 2.1 of the License, or
00015 //  (at your option) any later version.
00016 //  
00017 //  This program is distributed in the hope that it will be useful,
00018 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //  GNU Lesser General Public License for more details.
00021 //  
00022 //  You should have received a copy of the GNU Lesser General Public License
00023 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00024 //  
00025 //  See LICENSE.TXT file for more information.
00026 //  ----------------------------------------------------------------------------
00027 //
00028 // Description : This is a PHP class for generating PDF documents without 
00029 //               requiring external extensions.
00030 //
00031 // NOTE:
00032 // This class was originally derived in 2002 from the Public 
00033 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 
00034 // but now is almost entirely rewritten.
00035 //
00036 // Main features:
00037 //  * no external libraries are required for the basic functions;
00038 //  * supports all ISO page formats;
00039 //  * supports custom page formats, margins and units of measure;
00040 //  * supports UTF-8 Unicode and Right-To-Left languages;
00041 //  * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
00042 //  * supports document encryption;
00043 //  * includes methods to publish some XHTML code, including forms;
00044 //  * includes graphic (geometric) and transformation methods;
00045 //  * includes Javascript and Forms support;
00046 //  * includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;
00047 //  * includes methods to set Bookmarks and print a Table of Content;
00048 //  * includes methods to move and delete pages;
00049 //  * includes methods for automatic page header and footer management;
00050 //  * supports automatic page break;
00051 //  * supports automatic page numbering and page groups;
00052 //  * supports automatic line break and text justification;
00053 //  * supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
00054 //  * supports stroke and clipping mode for text;
00055 //  * supports clipping masks;
00056 //  * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;
00057 //  * supports several annotations, including links, text and file attachments;
00058 //  * supports page compression (requires zlib extension);
00059 //  * supports text hyphenation.
00060 //  * supports transactions to UNDO commands.
00061 //  * supports signature certifications.
00062 //
00063 // -----------------------------------------------------------
00064 // THANKS TO:
00065 // 
00066 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
00067 // Efthimios Mavrogeorgiadis ([email protected]) for suggestions on RTL language support.
00068 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
00069 // Warren Sherliker ([email protected]) for better image handling.
00070 // dullus for text Justification.
00071 // Bob Vincent ([email protected]) for <li> value attribute.
00072 // Patrick Benny for text stretch suggestion on Cell().
00073 // Johannes G�ntert for JavaScript support.
00074 // Denis Van Nuffelen for Dynamic Form.
00075 // Jacek Czekaj for multibyte justification
00076 // Anthony Ferrara for the reintroduction of legacy image methods.
00077 // Sourceforge user 1707880 (hucste) for line-trough mode.
00078 // Larry Stanbery for page groups.
00079 // Martin Hall-May for transparency.
00080 // Aaron C. Spike for Polycurve method.
00081 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
00082 // Moritz Wagner and Andreas Wurmser for graphic functions.
00083 // Andrew Whitehead for core fonts support.
00084 // Esteban Jo�l Mar�n for OpenType font conversion.
00085 // Teus Hagen for several suggestions and fixes.
00086 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
00087 // Kosmas Papachristos for some CSS improvements.
00088 // Marcel Partap for some fixes.
00089 // Won Kyu Park for several suggestions, fixes and patches.
00090 // Anyone that has reported a bug or sent a suggestion.
00091 //============================================================+
00092 
00136 require_once(dirname(__FILE__).'/config/tcpdf_config.php');
00137 
00138 // includes some support files
00139 
00143 require_once(dirname(__FILE__).'/unicode_data.php');
00144 
00148 require_once(dirname(__FILE__).'/htmlcolors.php');
00149 
00150 if (!class_exists('TCPDF', false)) {
00154     define('PDF_PRODUCER', 'TCPDF 4.8.007 (http://www.tcpdf.org)');
00155     
00165     class TCPDF {
00166         
00167         // protected or Protected properties
00168 
00173         protected $page;
00174         
00179         protected $n;
00180 
00185         protected $offsets;
00186 
00191         protected $buffer;
00192 
00197         protected $pages = array();
00198 
00203         protected $state;
00204 
00209         protected $compress;
00210         
00215         protected $CurOrientation;
00216 
00221         protected $pagedim = array();
00222 
00227         protected $k;
00228 
00233         protected $fwPt;
00234 
00239         protected $fhPt;
00240 
00245         protected $wPt;
00246 
00251         protected $hPt;
00252 
00257         protected $w;
00258 
00263         protected $h;
00264 
00269         protected $lMargin;
00270 
00275         protected $tMargin;
00276 
00281         protected $rMargin;
00282 
00287         protected $bMargin;
00288 
00293         //protected
00294         public $cMargin;
00295         
00300         protected $oldcMargin;
00301 
00306         protected $x;
00307 
00312         protected $y;
00313 
00318         protected $lasth;
00319 
00324         protected $LineWidth;
00325 
00330         protected $CoreFonts;
00331 
00336         protected $fonts = array();
00337 
00342         protected $FontFiles = array();
00343 
00348         protected $diffs = array();
00349 
00354         protected $images = array();
00355 
00360         protected $PageAnnots = array();
00361 
00366         protected $links = array();
00367 
00372         protected $FontFamily;
00373 
00378         protected $FontStyle;
00379         
00385         protected $FontAscent;
00386         
00392         protected $FontDescent;
00393 
00398         protected $underline;
00399 
00404         protected $CurrentFont;
00405 
00410         protected $FontSizePt;
00411 
00416         protected $FontSize;
00417 
00422         protected $DrawColor;
00423 
00428         protected $FillColor;
00429 
00434         protected $TextColor;
00435 
00440         protected $ColorFlag;
00441 
00446         protected $AutoPageBreak;
00447 
00452         protected $PageBreakTrigger;
00453 
00458         protected $InFooter = false;
00459 
00464         protected $ZoomMode;
00465 
00470         protected $LayoutMode;
00471 
00476         protected $title = '';
00477 
00482         protected $subject = '';
00483 
00488         protected $author = '';
00489 
00494         protected $keywords = '';
00495 
00500         protected $creator = '';
00501 
00506         protected $AliasNbPages = '{nb}';
00507         
00512         protected $AliasNumPage = '{pnb}';
00513         
00520         protected $img_rb_x;
00521 
00528         protected $img_rb_y;
00529 
00536         protected $imgscale = 1;
00537 
00544         protected $isunicode = false;
00545 
00551         protected $PDFVersion = '1.7';
00552         
00553         
00554         // ----------------------
00555         
00560         protected $header_margin;
00561         
00566         protected $footer_margin;
00567         
00573         protected $original_lMargin;
00574         
00580         protected $original_rMargin;
00581             
00586         protected $header_font;
00587         
00592         protected $footer_font;
00593         
00598         protected $l;
00599         
00604         protected $barcode = false;
00605         
00610         protected $print_header = true;
00611         
00616         protected $print_footer = true;
00617             
00622         protected $header_logo = '';
00623         
00628         protected $header_logo_width = 30;
00629         
00634         protected $header_title = '';
00635         
00640         protected $header_string = '';
00641         
00646         protected $default_table_columns = 4;
00647         
00648         
00649         // variables for html parser
00650         
00655         protected $HREF = array();
00656         
00661         protected $fontlist = array();
00662         
00667         protected $fgcolor;
00668                         
00673         protected $listordered = array();
00674         
00679         protected $listcount = array();
00680         
00685         protected $listnum = 0;
00686         
00691         protected $listindent;
00692         
00697         protected $bgcolor;
00698         
00703         protected $tempfontsize = 10;
00704         
00709         protected $lispacer = '';
00710         
00716         protected $encoding = 'UTF-8';
00717         
00723         protected $internal_encoding;
00724         
00730         protected $rtl = false;
00731         
00737         protected $tmprtl = false;
00738         
00739         // --- Variables used for document encryption:
00740         
00746         protected $encrypted;
00747         
00753         protected $Uvalue;
00754         
00760         protected $Ovalue;
00761         
00767         protected $Pvalue;
00768         
00774         protected $enc_obj_id;
00775         
00781         protected $last_rc4_key;
00782         
00788         protected $last_rc4_key_c;
00789         
00794         protected $padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
00795         
00800         protected $encryption_key;
00801         
00802         // --- bookmark ---
00803         
00809         protected $outlines = array();
00810         
00816         protected $OutlineRoot;
00817         
00818         
00819         // --- javascript and form ---
00820         
00826         protected $javascript = '';
00827         
00833         protected $n_js;
00834 
00840         protected $linethrough;
00841 
00842         // --- Variables used for User's Rights ---
00843         // See PDF reference chapter 8.7 Digital Signatures
00844 
00850         protected $ur;
00851 
00857         protected $ur_document;
00858 
00864         protected $ur_annots;
00865 
00871         protected $ur_form;
00872 
00878         protected $ur_signature;
00879 
00885         protected $dpi = 72;
00886         
00892         protected $newpagegroup = array();
00893         
00899         protected $pagegroups;
00900         
00906         protected $currpagegroup; 
00907         
00913         protected $visibility = 'all';
00914         
00920         protected $n_ocg_print;
00921         
00927         protected $n_ocg_view;
00928         
00934         protected $extgstates;
00935         
00941         protected $jpeg_quality;
00942         
00948         protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
00949         
00955         protected $viewer_preferences;
00956         
00962         protected $PageMode;
00963         
00969         protected $gradients = array();
00970         
00977         protected $intmrk = array();
00978         
00985         protected $cntmrk = array();
00986         
00992         protected $footerpos = array();
00993         
00994         
01000         protected $footerlen = array();
01001         
01007         protected $newline = true;
01008         
01014         protected $endlinex = 0;
01015         
01021         protected $linestyleWidth = '';
01022         
01028         protected $linestyleCap = '0 J';
01029         
01035         protected $linestyleJoin = '0 j';
01036         
01042         protected $linestyleDash = '[] 0 d';
01043         
01049         protected $openMarkedContent = false;
01050         
01056         protected $htmlvspace = 0;
01057         
01063         protected $spot_colors = array();
01064         
01070         protected $lisymbol = '';
01071         
01077         protected $epsmarker = 'x#!#EPS#!#x';
01078         
01084         protected $transfmatrix = array();
01085 
01091         protected $transfmatrix_key = 0;
01092 
01098         protected $booklet = false;
01099         
01105         protected $feps = 0.005;
01106         
01112         protected $tagvspaces = array();
01113         
01120         protected $customlistindent = -1;
01121         
01127         protected $opencell = true;
01128 
01134         protected $embeddedfiles = array();
01135 
01141         protected $premode = false;
01142 
01149         protected $transfmrk = array();
01150 
01156         protected $htmlLinkColorArray = array(0, 0, 255);
01157 
01163         protected $htmlLinkFontStyle = 'U';
01164 
01170         protected $numpages = 0;
01171 
01177         protected $pagelen = array();
01178 
01184         protected $numimages = 0;
01185 
01191         protected $imagekeys = array();
01192 
01198         protected $bufferlen = 0;
01199 
01205         protected $diskcache = false;
01206 
01212         protected $numfonts = 0;
01213 
01219         protected $fontkeys = array();
01220         
01226         protected $font_obj_ids = array();
01227 
01233         protected $pageopen = array();
01234         
01240         protected $default_monospaced_font = 'courier';
01241 
01247         protected $objcopy;
01248 
01254         protected $cache_file_lenght = array();
01255 
01261         protected $thead = '';
01262 
01268         protected $theadMargins = array();
01269 
01275         protected $cache_UTF8StringToArray = array();
01276 
01282         protected $cache_maxsize_UTF8StringToArray = 8;
01283 
01289         protected $cache_size_UTF8StringToArray = 0;
01290 
01296         protected $sign = false;
01297 
01303         protected $signature_data = array();
01304 
01310         protected $signature_max_lenght = 11742;
01311 
01317         protected $re_spaces = '/[\s]/';
01318 
01324         protected $sig_obj_id = 0;
01325 
01331         protected $byterange_string = '/ByteRange[0 ********** ********** **********]';
01332 
01338         protected $sig_annot_ref = '***SIGANNREF*** 0 R';
01339 
01345         protected $page_obj_id = array();
01346 
01352         protected $embedded_start_obj_id = 100000;
01353 
01359         protected $annots_start_obj_id = 200000;
01360         
01366         protected $annot_obj_id = 200000;
01367         
01373         protected $curr_annot_obj_id = 200000;
01374         
01380         protected $form_obj_id = array();
01381         
01382         /*
01383          * Deafult Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. Annotation options can be directly specified using the 'aopt' entry.
01384          * @access protected
01385          * @since 4.8.000 (2009-09-07)
01386          */
01387         protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
01388 
01394         protected $js_objects = array();
01395 
01401         protected $js_start_obj_id = 300000;
01402         
01408         protected $js_obj_id = 300000;
01409         
01415         protected $form_action = '';
01416 
01422         protected $form_enctype = 'application/x-www-form-urlencoded';
01423 
01429         protected $form_mode = 'post';
01430 
01436         protected $apxo_start_obj_id = 400000;
01437         
01443         protected $apxo_obj_id = 400000;
01444         
01450         protected $annotation_fonts = array();
01451         
01457         protected $radiobutton_groups = array();
01458         
01464         protected $radio_groups = array();
01465         
01471         protected $textindent = 0;
01472         
01478         protected $start_transaction_page = 0;
01479         
01480         //------------------------------------------------------------
01481         // METHODS
01482         //------------------------------------------------------------
01483 
01497         public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
01498             /* Set internal character encoding to ASCII */
01499             if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
01500                 $this->internal_encoding = mb_internal_encoding();
01501                 mb_internal_encoding('ASCII');
01502             }
01503             // set disk caching
01504             $this->diskcache = $diskcache ? true : false;
01505             // set language direction
01506             $this->rtl = false;
01507             $this->tmprtl = false;
01508             //Some checks
01509             $this->_dochecks();
01510             //Initialization of properties
01511             $this->isunicode = $unicode;
01512             $this->page = 0;
01513             $this->transfmrk[0] = array();
01514             $this->pagedim = array();
01515             $this->n = 2;
01516             $this->buffer = '';
01517             $this->pages = array();
01518             $this->state = 0;
01519             $this->fonts = array();
01520             $this->FontFiles = array();
01521             $this->diffs = array();
01522             $this->images = array();
01523             $this->links = array();
01524             $this->gradients = array();
01525             $this->InFooter = false;
01526             $this->lasth = 0;
01527             $this->FontFamily = 'helvetica';
01528             $this->FontStyle = '';
01529             $this->FontSizePt = 12;
01530             $this->underline = false;
01531             $this->linethrough = false;
01532             $this->DrawColor = '0 G';
01533             $this->FillColor = '0 g';
01534             $this->TextColor = '0 g';
01535             $this->ColorFlag = false;
01536             // encryption values
01537             $this->encrypted = false;
01538             $this->last_rc4_key = '';
01539             $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";
01540             //Standard Unicode fonts
01541             $this->CoreFonts = array(
01542                 'courier'=>'Courier',
01543                 'courierB'=>'Courier-Bold',
01544                 'courierI'=>'Courier-Oblique',
01545                 'courierBI'=>'Courier-BoldOblique',
01546                 'helvetica'=>'Helvetica',
01547                 'helveticaB'=>'Helvetica-Bold',
01548                 'helveticaI'=>'Helvetica-Oblique',
01549                 'helveticaBI'=>'Helvetica-BoldOblique',
01550                 'times'=>'Times-Roman',
01551                 'timesB'=>'Times-Bold',
01552                 'timesI'=>'Times-Italic',
01553                 'timesBI'=>'Times-BoldItalic',
01554                 'symbol'=>'Symbol',
01555                 'zapfdingbats'=>'ZapfDingbats'
01556             );
01557             //Set scale factor
01558             $this->setPageUnit($unit);
01559             // set page format and orientation
01560             $this->setPageFormat($format, $orientation);
01561             //Page margins (1 cm)
01562             $margin = 28.35 / $this->k;
01563             $this->SetMargins($margin, $margin);
01564             //Interior cell margin
01565             $this->cMargin = $margin / 10;
01566             //Line width (0.2 mm)
01567             $this->LineWidth = 0.57 / $this->k;
01568             $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
01569             $this->linestyleCap = '0 J';
01570             $this->linestyleJoin = '0 j';
01571             $this->linestyleDash = '[] 0 d';
01572             //Automatic page break
01573             $this->SetAutoPageBreak(true, (2 * $margin));
01574             //Full width display mode
01575             $this->SetDisplayMode('fullwidth');
01576             //Compression
01577             $this->SetCompression(true);
01578             //Set default PDF version number
01579             $this->PDFVersion = '1.7';
01580             $this->encoding = $encoding;
01581             $this->HREF = array();
01582             $this->getFontsList();
01583             $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
01584             $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
01585             $this->extgstates = array();
01586             // user's rights
01587             $this->sign = false;
01588             $this->ur = false;
01589             $this->ur_document = '/FullSave';
01590             $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export';
01591             $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
01592             $this->ur_signature = '/Modify';            
01593             // set default JPEG quality
01594             $this->jpeg_quality = 75;
01595             // initialize some settings
01596             $this->utf8Bidi(array(''), '');
01597             // set default font
01598             $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
01599             // check if PCRE Unicode support is enabled
01600             if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
01601                 // PCRE unicode support is turned ON
01602                 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
01603                 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
01604                 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
01605                 //$this->re_spaces = '/[\s\p{Z}\p{Lo}]/u';
01606                 $this->re_spaces = '/[\s\p{Z}]/u';
01607             } else {
01608                 // PCRE unicode support is turned OFF
01609                 $this->re_spaces = '/[\s]/';
01610             }
01611             $this->annot_obj_id = $this->annots_start_obj_id;
01612             $this->curr_annot_obj_id = $this->annots_start_obj_id;
01613             $this->apxo_obj_id = $this->apxo_start_obj_id;
01614             $this->js_obj_id = $this->js_start_obj_id;
01615             $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
01616         }
01617         
01623         public function __destruct() {
01624             // restore internal encoding
01625             if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
01626                 mb_internal_encoding($this->internal_encoding);
01627             }
01628             // unset all class variables
01629             $this->_destroy(true);
01630         }
01631         
01638         public function setPageUnit($unit) {
01639         //Set scale factor
01640             switch (strtolower($unit)) {
01641                 // points
01642                 case 'px':
01643                 case 'pt': {
01644                     $this->k = 1;
01645                     break;
01646                 }
01647                 // millimeters
01648                 case 'mm': {
01649                     $this->k = $this->dpi / 25.4;
01650                     break;
01651                 }
01652                 // centimeters
01653                 case 'cm': {
01654                     $this->k = $this->dpi / 2.54;
01655                     break;
01656                 }
01657                 // inches
01658                 case 'in': {
01659                     $this->k = $this->dpi;
01660                     break;
01661                 }
01662                 // unsupported unit
01663                 default : {
01664                     $this->Error('Incorrect unit: '.$unit);
01665                     break;
01666                 }
01667             }
01668             if (isset($this->CurOrientation)) {
01669                     $this->setPageOrientation($this->CurOrientation);
01670             }
01671         }
01672         
01680         public function setPageFormat($format, $orientation='P') {
01681             //Page format
01682             if (is_string($format)) {
01683                 // Page formats (45 standard ISO paper formats and 4 american common formats).
01684                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
01685                 switch (strtoupper($format)) {
01686                     case '4A0': {$format = array(4767.87,6740.79); break;}
01687                     case '2A0': {$format = array(3370.39,4767.87); break;}
01688                     case 'A0': {$format = array(2383.94,3370.39); break;}
01689                     case 'A1': {$format = array(1683.78,2383.94); break;}
01690                     case 'A2': {$format = array(1190.55,1683.78); break;}
01691                     case 'A3': {$format = array(841.89,1190.55); break;}
01692                     case 'A4': default: {$format = array(595.28,841.89); break;}
01693                     case 'A5': {$format = array(419.53,595.28); break;}
01694                     case 'A6': {$format = array(297.64,419.53); break;}
01695                     case 'A7': {$format = array(209.76,297.64); break;}
01696                     case 'A8': {$format = array(147.40,209.76); break;}
01697                     case 'A9': {$format = array(104.88,147.40); break;}
01698                     case 'A10': {$format = array(73.70,104.88); break;}
01699                     case 'B0': {$format = array(2834.65,4008.19); break;}
01700                     case 'B1': {$format = array(2004.09,2834.65); break;}
01701                     case 'B2': {$format = array(1417.32,2004.09); break;}
01702                     case 'B3': {$format = array(1000.63,1417.32); break;}
01703                     case 'B4': {$format = array(708.66,1000.63); break;}
01704                     case 'B5': {$format = array(498.90,708.66); break;}
01705                     case 'B6': {$format = array(354.33,498.90); break;}
01706                     case 'B7': {$format = array(249.45,354.33); break;}
01707                     case 'B8': {$format = array(175.75,249.45); break;}
01708                     case 'B9': {$format = array(124.72,175.75); break;}
01709                     case 'B10': {$format = array(87.87,124.72); break;}
01710                     case 'C0': {$format = array(2599.37,3676.54); break;}
01711                     case 'C1': {$format = array(1836.85,2599.37); break;}
01712                     case 'C2': {$format = array(1298.27,1836.85); break;}
01713                     case 'C3': {$format = array(918.43,1298.27); break;}
01714                     case 'C4': {$format = array(649.13,918.43); break;}
01715                     case 'C5': {$format = array(459.21,649.13); break;}
01716                     case 'C6': {$format = array(323.15,459.21); break;}
01717                     case 'C7': {$format = array(229.61,323.15); break;}
01718                     case 'C8': {$format = array(161.57,229.61); break;}
01719                     case 'C9': {$format = array(113.39,161.57); break;}
01720                     case 'C10': {$format = array(79.37,113.39); break;}
01721                     case 'RA0': {$format = array(2437.80,3458.27); break;}
01722                     case 'RA1': {$format = array(1729.13,2437.80); break;}
01723                     case 'RA2': {$format = array(1218.90,1729.13); break;}
01724                     case 'RA3': {$format = array(864.57,1218.90); break;}
01725                     case 'RA4': {$format = array(609.45,864.57); break;}
01726                     case 'SRA0': {$format = array(2551.18,3628.35); break;}
01727                     case 'SRA1': {$format = array(1814.17,2551.18); break;}
01728                     case 'SRA2': {$format = array(1275.59,1814.17); break;}
01729                     case 'SRA3': {$format = array(907.09,1275.59); break;}
01730                     case 'SRA4': {$format = array(637.80,907.09); break;}
01731                     case 'LETTER': {$format = array(612.00,792.00); break;}
01732                     case 'LEGAL': {$format = array(612.00,1008.00); break;}
01733                     case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
01734                     case 'FOLIO': {$format = array(612.00,936.00); break;}
01735                 }
01736                 $this->fwPt = $format[0];
01737                 $this->fhPt = $format[1];
01738             } else {
01739                 $this->fwPt = $format[0] * $this->k;
01740                 $this->fhPt = $format[1] * $this->k;
01741             }
01742             $this->setPageOrientation($orientation);
01743         }
01744         
01753         public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
01754             $orientation = strtoupper($orientation);
01755             if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
01756                 $this->CurOrientation = 'P';
01757                 $this->wPt = $this->fwPt;
01758                 $this->hPt = $this->fhPt;
01759             } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
01760                 $this->CurOrientation = 'L';
01761                 $this->wPt = $this->fhPt;
01762                 $this->hPt = $this->fwPt;
01763             } else {
01764                 $this->Error('Incorrect orientation: '.$orientation);
01765             }
01766             $this->w = $this->wPt / $this->k;
01767             $this->h = $this->hPt / $this->k;
01768             if ($this->empty_string($autopagebreak)) {
01769                 if (isset($this->AutoPageBreak)) {
01770                     $autopagebreak = $this->AutoPageBreak;
01771                 } else {
01772                     $autopagebreak = true;
01773                 }
01774             }
01775             if ($this->empty_string($bottommargin)) {
01776                 if (isset($this->bMargin)) {
01777                     $bottommargin = $this->bMargin;
01778                 } else {
01779                     // default value = 2 cm
01780                     $bottommargin = 2 * 28.35 / $this->k;
01781                 }
01782             }
01783             $this->SetAutoPageBreak($autopagebreak, $bottommargin);
01784             // store page dimensions
01785             $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'wk' => $this->w, 'hk' => $this->h, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation, 'olm' => $this->original_lMargin, 'orm' => $this->original_rMargin);
01786         }
01787         
01794         public function setSpacesRE($re='/[\s]/') {
01795             // if PCRE unicode support is turned ON:
01796             //  \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
01797             //  \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
01798             //  \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
01799             $this->re_spaces = $re;
01800         }
01801             
01809         public function setRTL($enable, $resetx=true) {
01810             $enable = $enable ? true : false;
01811             $resetx = ($resetx AND ($enable != $this->rtl));
01812             $this->rtl = $enable;
01813             $this->tmprtl = false;
01814             if ($resetx) {
01815                 $this->Ln(0);
01816             }
01817         }
01818         
01825         public function getRTL() {
01826             return $this->rtl;
01827         }
01828         
01835         public function setTempRTL($mode) {
01836             switch ($mode) {
01837                 case false:
01838                 case 'L':
01839                 case 'R': {
01840                     $this->tmprtl = $mode;
01841                 }
01842             }
01843         }
01844         
01852         public function setLastH($h) {
01853             $this->lasth = $h;
01854         }
01855         
01862         public function getLastH() {
01863             return $this->lasth;
01864         }
01865         
01873         public function setImageScale($scale) {
01874             $this->imgscale = $scale;
01875         }
01876 
01884         public function getImageScale() {
01885             return $this->imgscale;
01886         }
01887                 
01897         public function getPageDimensions($pagenum='') {
01898             if (empty($pagenum)) {
01899                 $pagenum = $this->page;
01900             }
01901             return $this->pagedim[$pagenum];
01902         }
01903         
01913         public function getPageWidth($pagenum='') {
01914             if (empty($pagenum)) {
01915                 return $this->w;
01916             }
01917             return $this->pagedim[$pagenum]['w'];
01918         }
01919 
01929         public function getPageHeight($pagenum='') {
01930             if (empty($pagenum)) {
01931                 return $this->h;
01932             }
01933             return $this->pagedim[$pagenum]['h'];
01934         }
01935 
01945         public function getBreakMargin($pagenum='') {
01946             if (empty($pagenum)) {
01947                 return $this->bMargin;
01948             }
01949             return $this->pagedim[$pagenum]['bm'];
01950         }
01951 
01959         public function getScaleFactor() {
01960             return $this->k;
01961         }
01962 
01972         public function SetMargins($left, $top, $right=-1) {
01973             //Set left, top and right margins
01974             $this->lMargin = $left;
01975             $this->tMargin = $top;
01976             if ($right == -1) {
01977                 $right = $left;
01978             }
01979             $this->rMargin = $right;
01980         }
01981 
01989         public function SetLeftMargin($margin) {
01990             //Set left margin
01991             $this->lMargin=$margin;
01992             if (($this->page > 0) AND ($this->x < $margin)) {
01993                 $this->x = $margin;
01994             }
01995         }
01996 
02004         public function SetTopMargin($margin) {
02005             //Set top margin
02006             $this->tMargin=$margin;
02007             if (($this->page > 0) AND ($this->y < $margin)) {
02008                 $this->y = $margin;
02009             }
02010         }
02011 
02019         public function SetRightMargin($margin) {
02020             $this->rMargin=$margin;
02021             if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
02022                 $this->x = $this->w - $margin;
02023             }
02024         }
02025 
02033         public function SetCellPadding($pad) {
02034             $this->cMargin = $pad;
02035         }
02036 
02045         public function SetAutoPageBreak($auto, $margin=0) {
02046             //Set auto page break mode and triggering margin
02047             $this->AutoPageBreak = $auto;
02048             $this->bMargin = $margin;
02049             $this->PageBreakTrigger = $this->h - $margin;
02050         }
02051 
02060         public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
02061             //Set display mode in viewer
02062             if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
02063                 $this->ZoomMode = $zoom;
02064             } else {
02065                 $this->Error('Incorrect zoom display mode: '.$zoom);
02066             }
02067             switch ($layout) {
02068                 case 'default':
02069                 case 'single':
02070                 case 'SinglePage': {
02071                     $this->LayoutMode = 'SinglePage';
02072                     break;
02073                 }
02074                 case 'continuous':
02075                 case 'OneColumn': {
02076                     $this->LayoutMode = 'OneColumn';
02077                     break;
02078                 }
02079                 case 'two':
02080                 case 'TwoColumnLeft': {
02081                     $this->LayoutMode = 'TwoColumnLeft';
02082                     break;
02083                 }
02084                 case 'TwoColumnRight': {
02085                     $this->LayoutMode = 'TwoColumnRight';
02086                     break;
02087                 }
02088                 case 'TwoPageLeft': {
02089                     $this->LayoutMode = 'TwoPageLeft';
02090                     break;
02091                 }
02092                 case 'TwoPageRight': {
02093                     $this->LayoutMode = 'TwoPageRight';
02094                     break;
02095                 }
02096                 default: {
02097                     $this->LayoutMode = 'SinglePage';
02098                 }
02099             }
02100             switch ($mode) {
02101                 case 'UseNone': {
02102                     $this->PageMode = 'UseNone';
02103                     break;
02104                 }
02105                 case 'UseOutlines': {
02106                     $this->PageMode = 'UseOutlines';
02107                     break;
02108                 }
02109                 case 'UseThumbs': {
02110                     $this->PageMode = 'UseThumbs';
02111                     break;
02112                 }
02113                 case 'FullScreen': {
02114                     $this->PageMode = 'FullScreen';
02115                     break;
02116                 }
02117                 case 'UseOC': {
02118                     $this->PageMode = 'UseOC';
02119                     break;
02120                 }
02121                 case '': {
02122                     $this->PageMode = 'UseAttachments';
02123                     break;
02124                 }
02125                 default: {
02126                     $this->PageMode = 'UseNone';
02127                 }
02128             }
02129         }
02130 
02138         public function SetCompression($compress) {
02139             //Set page compression
02140             if (function_exists('gzcompress')) {
02141                 $this->compress = $compress;
02142             } else {
02143                 $this->compress = false;
02144             }
02145         }
02146 
02154         public function SetTitle($title) {
02155             //Title of document
02156             $this->title = $title;
02157         }
02158 
02166         public function SetSubject($subject) {
02167             //Subject of document
02168             $this->subject = $subject;
02169         }
02170 
02178         public function SetAuthor($author) {
02179             //Author of document
02180             $this->author = $author;
02181         }
02182 
02190         public function SetKeywords($keywords) {
02191             //Keywords of document
02192             $this->keywords = $keywords;
02193         }
02194 
02202         public function SetCreator($creator) {
02203             //Creator of document
02204             $this->creator = $creator;
02205         }
02206         
02214         public function Error($msg) {
02215             // unset all class variables
02216             $this->_destroy(true);
02217             // exit program and print error
02218             die('<strong>TCPDF ERROR: </strong>'.$msg);
02219         }
02220 
02229         public function Open() {
02230             //Begin document
02231             $this->state = 1;
02232         }
02233 
02242         public function Close() {
02243             if ($this->state == 3) {
02244                 return;
02245             }
02246             if ($this->page == 0) {
02247                 $this->AddPage();
02248             }
02249             // close page
02250             $this->endPage();
02251             // close document
02252             $this->_enddoc();
02253             // unset all class variables (except critical ones)
02254             $this->_destroy(false);
02255         }
02256         
02265         public function setPage($pnum, $resetmargins=false) {
02266             if ($pnum == $this->page) {
02267                 return;
02268             }
02269             if (($pnum > 0) AND ($pnum <= $this->numpages)) {
02270                 $this->state = 2;
02271                 // save current graphic settings
02272                 //$gvars = $this->getGraphicVars();
02273                 $oldpage = $this->page;
02274                 $this->page = $pnum;
02275                 $this->wPt = $this->pagedim[$this->page]['w'];
02276                 $this->hPt = $this->pagedim[$this->page]['h'];
02277                 $this->w = $this->wPt / $this->k;
02278                 $this->h = $this->hPt / $this->k;
02279                 $this->tMargin = $this->pagedim[$this->page]['tm'];
02280                 $this->bMargin = $this->pagedim[$this->page]['bm'];
02281                 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
02282                 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
02283                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
02284                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
02285                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
02286                 // restore graphic settings
02287                 //$this->setGraphicVars($gvars);
02288                 if ($resetmargins) {
02289                     $this->lMargin = $this->pagedim[$this->page]['olm'];
02290                     $this->rMargin = $this->pagedim[$this->page]['orm'];
02291                     $this->SetY($this->tMargin);
02292                 } else {
02293                     // account for booklet mode
02294                     if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
02295                         $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
02296                         $this->lMargin += $deltam;
02297                         $this->rMargin -= $deltam;
02298                     }
02299                 }
02300             } else {
02301                 $this->Error('Wrong page number on setPage() function.');
02302             }
02303         }
02304         
02312         public function lastPage($resetmargins=false) {
02313             $this->setPage($this->getNumPages(), $resetmargins);
02314         }
02315         
02323         public function getPage() {
02324             return $this->page;
02325         }
02326         
02327         
02335         public function getNumPages() {
02336             return $this->numpages;
02337         }
02338 
02348         public function AddPage($orientation='', $format='') {
02349             if (!isset($this->original_lMargin)) {
02350                 $this->original_lMargin = $this->lMargin;
02351             }
02352             if (!isset($this->original_rMargin)) {
02353                 $this->original_rMargin = $this->rMargin;
02354             }
02355             // terminate previous page
02356             $this->endPage();
02357             // start new page
02358             $this->startPage($orientation, $format);
02359         }
02360 
02367         protected function endPage() {
02368             // check if page is already closed
02369             if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
02370                 return;
02371             }
02372             $this->InFooter = true;
02373             // print page footer
02374             $this->setFooter();
02375             // close page
02376             $this->_endpage();
02377             // mark page as closed
02378             $this->pageopen[$this->page] = false;
02379             $this->InFooter = false;
02380         }
02381 
02391         protected function startPage($orientation='', $format='') {
02392             if ($this->numpages > $this->page) {
02393                 // this page has been already added
02394                 $this->setPage($this->page + 1);
02395                 $this->SetY($this->tMargin);
02396                 return;
02397             }
02398             // start a new page
02399             if ($this->state == 0) {
02400                 $this->Open();
02401             }
02402             ++$this->numpages;
02403             $this->swapMargins($this->booklet);
02404             // save current graphic settings
02405             $gvars = $this->getGraphicVars();
02406             // start new page
02407             $this->_beginpage($orientation, $format);
02408             // mark page as open
02409             $this->pageopen[$this->page] = true;
02410             // restore graphic settings
02411             $this->setGraphicVars($gvars);
02412             // mark this point
02413             $this->setPageMark();
02414             // print page header
02415             $this->setHeader();
02416             // restore graphic settings
02417             $this->setGraphicVars($gvars);
02418             // mark this point
02419             $this->setPageMark();
02420             // print table header (if any)
02421             $this->setTableHeader();
02422         }
02423             
02431         public function setPageMark() {
02432             $this->intmrk[$this->page] = $this->pagelen[$this->page];
02433             $this->setContentMark();
02434         }
02435         
02442         protected function setContentMark($page=0) {
02443             if ($page <= 0) {
02444                 $page = $this->page;
02445             }
02446             if (isset($this->footerlen[$page])) {
02447                 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
02448             } else {
02449                 $this->cntmrk[$page] = $this->pagelen[$page];
02450             }
02451         }
02452         
02461         public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
02462             $this->header_logo = $ln;
02463             $this->header_logo_width = $lw;
02464             $this->header_title = $ht;
02465             $this->header_string = $hs;
02466         }
02467         
02475         public function getHeaderData() {
02476             $ret = array();
02477             $ret['logo'] = $this->header_logo;
02478             $ret['logo_width'] = $this->header_logo_width;
02479             $ret['title'] = $this->header_title;
02480             $ret['string'] = $this->header_string;
02481             return $ret;
02482         }
02483         
02490         public function setHeaderMargin($hm=10) {
02491             $this->header_margin = $hm;
02492         }
02493         
02500         public function getHeaderMargin() {
02501             return $this->header_margin;
02502         }
02503         
02510         public function setFooterMargin($fm=10) {
02511             $this->footer_margin = $fm;
02512         }
02513         
02520         public function getFooterMargin() {
02521             return $this->footer_margin;
02522         }
02528         public function setPrintHeader($val=true) {
02529             $this->print_header = $val;
02530         }
02531         
02537         public function setPrintFooter($val=true) {
02538             $this->print_footer = $val;
02539         }
02540         
02546         public function getImageRBX() {
02547             return $this->img_rb_x;
02548         }
02549         
02555         public function getImageRBY() {
02556             return $this->img_rb_y;
02557         }
02558         
02564         public function Header() {
02565             $ormargins = $this->getOriginalMargins();
02566             $headerfont = $this->getHeaderFont();
02567             $headerdata = $this->getHeaderData();
02568             if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
02569                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
02570                 $imgy = $this->getImageRBY();
02571             } else {
02572                 $imgy = $this->GetY();
02573             }
02574             $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
02575             // set starting margin for text data cell
02576             if ($this->getRTL()) {
02577                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
02578             } else {
02579                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
02580             }
02581             $this->SetTextColor(0, 0, 0);
02582             // header title
02583             $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
02584             $this->SetX($header_x);         
02585             $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
02586             // header string
02587             $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
02588             $this->SetX($header_x);
02589             $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
02590             // print an ending header line
02591             $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
02592             $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
02593             if ($this->getRTL()) {
02594                 $this->SetX($ormargins['right']);
02595             } else {
02596                 $this->SetX($ormargins['left']);
02597             }
02598             $this->Cell(0, 0, '', 'T', 0, 'C');
02599         }
02600         
02606         public function Footer() {              
02607             $cur_y = $this->GetY();
02608             $ormargins = $this->getOriginalMargins();
02609             $this->SetTextColor(0, 0, 0);           
02610             //set style for cell border
02611             $line_width = 0.85 / $this->getScaleFactor();
02612             $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
02613             //print document barcode
02614             $barcode = $this->getBarcode();
02615             if (!empty($barcode)) {
02616                 $this->Ln($line_width);
02617                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
02618                 $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', ''); 
02619             }
02620             if (empty($this->pagegroups)) {
02621                 $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
02622             } else {
02623                 $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
02624             }       
02625             $this->SetY($cur_y);
02626             //Print page number
02627             if ($this->getRTL()) {
02628                 $this->SetX($ormargins['right']);
02629                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
02630             } else {
02631                 $this->SetX($ormargins['left']);
02632                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
02633             }
02634         }
02635         
02641         protected function setHeader() {
02642             if ($this->print_header) {
02643                 $lasth = $this->lasth;
02644                 $this->_out('q');
02645                 $this->rMargin = $this->original_rMargin;
02646                 $this->lMargin = $this->original_lMargin;
02647                 $this->cMargin = 0;
02648                 //set current position
02649                 if ($this->rtl) {
02650                     $this->SetXY($this->original_rMargin, $this->header_margin);
02651                 } else {
02652                     $this->SetXY($this->original_lMargin, $this->header_margin);
02653                 }
02654                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
02655                 $this->Header();
02656                 //restore position
02657                 if ($this->rtl) {
02658                     $this->SetXY($this->original_rMargin, $this->tMargin);
02659                 } else {
02660                     $this->SetXY($this->original_lMargin, $this->tMargin);
02661                 }
02662                 $this->_out('Q');
02663                 $this->lasth = $lasth;
02664             }
02665         }
02666         
02672         protected function setFooter() {
02673             //Page footer
02674             // save current graphic settings
02675             $gvars = $this->getGraphicVars();
02676             // mark this point
02677             $this->footerpos[$this->page] = $this->pagelen[$this->page];
02678             $this->_out("\n");
02679             if ($this->print_footer) {
02680                 $lasth = $this->lasth;
02681                 $this->_out('q');
02682                 $this->rMargin = $this->original_rMargin;
02683                 $this->lMargin = $this->original_lMargin;
02684                 $this->cMargin = 0;
02685                 //set current position
02686                 $footer_y = $this->h - $this->footer_margin;
02687                 if ($this->rtl) {
02688                     $this->SetXY($this->original_rMargin, $footer_y);
02689                 } else {
02690                     $this->SetXY($this->original_lMargin, $footer_y);
02691                 }
02692                 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
02693                 $this->Footer();
02694                 //restore position
02695                 if ($this->rtl) {
02696                     $this->SetXY($this->original_rMargin, $this->tMargin);
02697                 } else {
02698                     $this->SetXY($this->original_lMargin, $this->tMargin);
02699                 }
02700                 $this->_out('Q');
02701                 $this->lasth = $lasth;
02702             }
02703             // restore graphic settings
02704             $this->setGraphicVars($gvars);
02705             // calculate footer lenght
02706             $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
02707         }
02708 
02714         protected function setTableHeader() {
02715             if (isset($this->theadMargins['top'])) {
02716                 // restore the original top-margin
02717                 $this->tMargin = $this->theadMargins['top'];
02718                 $this->pagedim[$this->page]['tm'] = $this->tMargin;
02719                 $this->y = $this->tMargin;
02720             }
02721             if (!$this->empty_string($this->thead)) {
02722                 // set margins
02723                 $prev_lMargin = $this->lMargin;
02724                 $prev_rMargin = $this->rMargin;
02725                 $this->lMargin = $this->pagedim[$this->page]['olm'];
02726                 $this->rMargin = $this->pagedim[$this->page]['orm'];
02727                 $this->cMargin = $this->theadMargins['cmargin'];
02728                 // print table header
02729                 $this->writeHTML($this->thead, false, false, false, false, '');
02730                 // set new top margin to skip the table headers
02731                 if (!isset($this->theadMargins['top'])) {
02732                     $this->theadMargins['top'] = $this->tMargin;
02733                 }
02734                 $this->tMargin = $this->y;
02735                 $this->pagedim[$this->page]['tm'] = $this->tMargin;
02736                 $this->lasth = 0;
02737                 $this->lMargin = $prev_lMargin;
02738                 $this->rMargin = $prev_rMargin;
02739             }
02740         }
02741         
02749         public function PageNo() {
02750             return $this->page;
02751         }
02752 
02765         public function AddSpotColor($name, $c, $m, $y, $k) {
02766             if (!isset($this->spot_colors[$name])) {
02767                 $i = 1 + count($this->spot_colors);
02768                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
02769             }
02770         }
02771 
02781         public function SetDrawColorArray($color) {
02782             if (isset($color)) {
02783                 $color = array_values($color);
02784                 $r = isset($color[0]) ? $color[0] : -1;
02785                 $g = isset($color[1]) ? $color[1] : -1;
02786                 $b = isset($color[2]) ? $color[2] : -1;
02787                 $k = isset($color[3]) ? $color[3] : -1;
02788                 if ($r >= 0) {
02789                     $this->SetDrawColor($r, $g, $b, $k);
02790                 }
02791             }
02792         }
02793 
02804         public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
02805             // set default values
02806             if (!is_numeric($col1)) {
02807                 $col1 = 0;
02808             }
02809             if (!is_numeric($col2)) {
02810                 $col2 = -1;
02811             }
02812             if (!is_numeric($col3)) {
02813                 $col3 = -1;
02814             }
02815             if (!is_numeric($col4)) {
02816                 $col4 = -1;
02817             }
02818             //Set color for all stroking operations
02819             if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
02820                 // Grey scale
02821                 $this->DrawColor = sprintf('%.3F G', $col1/255);
02822             } elseif ($col4 == -1) {
02823                 // RGB
02824                 $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
02825             } else {
02826                 // CMYK
02827                 $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
02828             }
02829             if ($this->page > 0) {
02830                 $this->_out($this->DrawColor);
02831             }
02832         }
02833         
02842         public function SetDrawSpotColor($name, $tint=100) {
02843             if (!isset($this->spot_colors[$name])) {
02844                 $this->Error('Undefined spot color: '.$name);
02845             }
02846             $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
02847             if ($this->page > 0) {
02848                 $this->_out($this->DrawColor);
02849             }
02850         }
02851         
02861         public function SetFillColorArray($color) {
02862             if (isset($color)) {
02863                 $color = array_values($color);
02864                 $r = isset($color[0]) ? $color[0] : -1;
02865                 $g = isset($color[1]) ? $color[1] : -1;
02866                 $b = isset($color[2]) ? $color[2] : -1;
02867                 $k = isset($color[3]) ? $color[3] : -1;
02868                 if ($r >= 0) {
02869                     $this->SetFillColor($r, $g, $b, $k);
02870                 }
02871             }
02872         }
02873         
02884         public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
02885             // set default values
02886             if (!is_numeric($col1)) {
02887                 $col1 = 0;
02888             }
02889             if (!is_numeric($col2)) {
02890                 $col2 = -1;
02891             }
02892             if (!is_numeric($col3)) {
02893                 $col3 = -1;
02894             }
02895             if (!is_numeric($col4)) {
02896                 $col4 = -1;
02897             }
02898             //Set color for all filling operations
02899             if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
02900                 // Grey scale
02901                 $this->FillColor = sprintf('%.3F g', $col1/255);
02902                 $this->bgcolor = array('G' => $col1);
02903             } elseif ($col4 == -1) {
02904                 // RGB
02905                 $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
02906                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
02907             } else {
02908                 // CMYK
02909                 $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
02910                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
02911             }
02912             $this->ColorFlag = ($this->FillColor != $this->TextColor);
02913             if ($this->page > 0) {
02914                 $this->_out($this->FillColor);
02915             }
02916         }
02917         
02926         public function SetFillSpotColor($name, $tint=100) {
02927             if (!isset($this->spot_colors[$name])) {
02928                 $this->Error('Undefined spot color: '.$name);
02929             }
02930             $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
02931             $this->ColorFlag = ($this->FillColor != $this->TextColor);
02932             if ($this->page > 0) {
02933                 $this->_out($this->FillColor);
02934             }
02935         }
02936         
02945         public function SetTextColorArray($color) {
02946             if (isset($color)) {
02947                 $color = array_values($color);
02948                 $r = isset($color[0]) ? $color[0] : -1;
02949                 $g = isset($color[1]) ? $color[1] : -1;
02950                 $b = isset($color[2]) ? $color[2] : -1;
02951                 $k = isset($color[3]) ? $color[3] : -1;
02952                 if ($r >= 0) {
02953                     $this->SetTextColor($r, $g, $b, $k);
02954                 }
02955             }
02956         }
02957 
02968         public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
02969             // set default values
02970             if (!is_numeric($col1)) {
02971                 $col1 = 0;
02972             }
02973             if (!is_numeric($col2)) {
02974                 $col2 = -1;
02975             }
02976             if (!is_numeric($col3)) {
02977                 $col3 = -1;
02978             }
02979             if (!is_numeric($col4)) {
02980                 $col4 = -1;
02981             }
02982             //Set color for text
02983             if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
02984                 // Grey scale
02985                 $this->TextColor = sprintf('%.3F g', $col1/255);
02986                 $this->fgcolor = array('G' => $col1);
02987             } elseif ($col4 == -1) {
02988                 // RGB
02989                 $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
02990                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
02991             } else {
02992                 // CMYK
02993                 $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
02994                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
02995             }
02996             $this->ColorFlag = ($this->FillColor != $this->TextColor);
02997         }
02998         
03007         public function SetTextSpotColor($name, $tint=100) {
03008             if (!isset($this->spot_colors[$name])) {
03009                 $this->Error('Undefined spot color: '.$name);
03010             }
03011             $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
03012             $this->ColorFlag = ($this->FillColor != $this->TextColor);
03013             if ($this->page > 0) {
03014                 $this->_out($this->TextColor);
03015             }
03016         }
03017 
03029         public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
03030             return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize);
03031         }
03032         
03044         public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
03045             // store current values
03046             if (!$this->empty_string($fontname)) {
03047                 $prev_FontFamily = $this->FontFamily;
03048                 $prev_FontStyle = $this->FontStyle;
03049                 $prev_FontSizePt = $this->FontSizePt;
03050                 $this->SetFont($fontname, $fontstyle, $fontsize);
03051             }
03052             $w = 0;
03053             foreach ($sa as $char) {
03054                 $w += $this->GetCharWidth($char);
03055             }
03056             // restore previous values
03057             if (!$this->empty_string($fontname)) {
03058                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
03059             }
03060             return $w;
03061         }
03062         
03071         public function GetCharWidth($char) {
03072             if ($char == 173) {
03073                 // SHY character will not be printed
03074                 return (0);
03075             }
03076             $cw = &$this->CurrentFont['cw'];
03077             if (isset($cw[$char])) {
03078                 $w = $cw[$char];
03079             } elseif (isset($this->CurrentFont['dw'])) {
03080                 // default width
03081                 $w = $this->CurrentFont['dw'];
03082             } elseif (isset($cw[32])) {
03083                 // default width
03084                 $dw = $cw[32];
03085             } else {
03086                 $w = 600;
03087             }
03088             return ($w * $this->FontSize / 1000);
03089         }
03090         
03098         public function GetNumChars($s) {
03099             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
03100                 return count($this->UTF8StringToArray($s));
03101             } 
03102             return strlen($s);
03103         }
03104             
03110         protected function getFontsList() {
03111             $fontsdir = opendir($this->_getfontpath());
03112             while (($file = readdir($fontsdir)) !== false) {
03113                 if (substr($file, -4) == '.php') {
03114                     array_push($this->fontlist, strtolower(basename($file, '.php')));
03115                 }
03116             }
03117             closedir($fontsdir);
03118         }
03119         
03132         public function AddFont($family, $style='', $fontfile='') {
03133             if ($this->empty_string($family)) {
03134                 if (!$this->empty_string($this->FontFamily)) {
03135                     $family = $this->FontFamily;
03136                 } else {
03137                     $this->Error('Empty font family');
03138                 }
03139             }
03140             $family = strtolower($family);
03141             if ((!$this->isunicode) AND ($family == 'arial')) {
03142                 $family = 'helvetica';
03143             }
03144             if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
03145                 $style = '';
03146             }
03147             $tempstyle = strtoupper($style);
03148             $style = '';
03149             // underline
03150             if (strpos($tempstyle, 'U') !== false) {
03151                 $this->underline = true;
03152             } else {
03153                 $this->underline = false;
03154             }
03155             // line through (deleted)
03156             if (strpos($tempstyle, 'D') !== false) {
03157                 $this->linethrough = true;
03158             } else {
03159                 $this->linethrough = false;
03160             }
03161             // bold
03162             if (strpos($tempstyle, 'B') !== false) {
03163                 $style .= 'B';
03164             }
03165             // oblique
03166             if (strpos($tempstyle, 'I') !== false) {
03167                 $style .= 'I';
03168             }
03169             $bistyle = $style;
03170             $fontkey = $family.$style;
03171             $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
03172             $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
03173             // check if the font has been already added
03174             if ($this->getFontBuffer($fontkey) !== false) {
03175                 return $fontdata;
03176             }
03177             if (isset($type)) {
03178                 unset($type); 
03179             }
03180             if (isset($cw)) {
03181                 unset($cw); 
03182             }
03183             // get specified font directory (if any)
03184             $fontdir = '';
03185             if (!$this->empty_string($fontfile)) {
03186                 $fontdir = dirname($fontfile);
03187                 if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
03188                     $fontdir = '';
03189                 } else {
03190                     $fontdir .= '/';
03191                 }
03192             }
03193             // search and include font file
03194             if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
03195                 // build a standard filenames for specified font
03196                 $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
03197                 $fontfile2 = str_replace(' ', '', $family).'.php';
03198                 // search files on various directories
03199                 if (file_exists($fontdir.$fontfile1)) {
03200                     $fontfile = $fontdir.$fontfile1;
03201                 } elseif (file_exists($this->_getfontpath().$fontfile1)) {
03202                     $fontfile = $this->_getfontpath().$fontfile1;
03203                 } elseif (file_exists($fontfile1)) {
03204                     $fontfile = $fontfile1;
03205                 } elseif (file_exists($fontdir.$fontfile2)) {
03206                     $fontfile = $fontdir.$fontfile2;
03207                 } elseif (file_exists($this->_getfontpath().$fontfile2)) {
03208                     $fontfile = $this->_getfontpath().$fontfile2;
03209                 } else {
03210                     $fontfile = $fontfile2;
03211                 }
03212             }
03213             // include font file
03214             if (file_exists($fontfile)) {
03215                 include($fontfile);
03216             } else {
03217                 $this->Error('Could not include font definition file: '.$family.'');
03218             }
03219             // check font parameters
03220             if ((!isset($type)) OR (!isset($cw))) {
03221                 $this->Error('The font definition file has a bad format: '.$fontfile.'');
03222             }
03223             // SET default parameters
03224             if (!isset($file) OR $this->empty_string($file)) {
03225                 $file = '';
03226             }
03227             if (!isset($enc) OR $this->empty_string($enc)) {
03228                 $enc = '';
03229             }
03230             if (!isset($cidinfo) OR $this->empty_string($cidinfo)) {
03231                 $cidinfo = array('Registry'=>'Adobe','Ordering'=>'Identity','Supplement'=>0);
03232                 $cidinfo['uni2cid'] = array();
03233             }
03234             if (!isset($ctg) OR $this->empty_string($ctg)) {
03235                 $ctg = '';
03236             }
03237             if (!isset($desc) OR $this->empty_string($desc)) {
03238                 $desc = array();
03239             }
03240             if (!isset($up) OR $this->empty_string($up)) {
03241                 $up = -100;
03242             }
03243             if (!isset($ut) OR $this->empty_string($ut)) {
03244                 $ut = 50;
03245             }
03246             if (!isset($cw) OR $this->empty_string($cw)) {
03247                 $cw = array();
03248             }
03249             if (!isset($dw) OR $this->empty_string($dw)) {
03250                 // set default width
03251                 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
03252                     $dw = $desc['MissingWidth'];
03253                 } elseif (isset($cw[32])) {
03254                     $dw = $cw[32];
03255                 } else {
03256                     $dw = 600;
03257                 }
03258             }
03259             ++$this->numfonts;          
03260             if ($type == 'cidfont0') {
03261                 // register CID font (all styles at once)
03262                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
03263                 $sname = $name.$styles[$bistyle];
03264                 if ((strpos($bistyle, 'B') !== false) AND (isset($desc['StemV'])) AND ($desc['StemV'] == 70)) {
03265                     $desc['StemV'] = 120;
03266                 }
03267             } elseif ($type == 'core') {
03268                 $name = $this->CoreFonts[$fontkey];
03269             } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
03270                 // ...
03271             } elseif ($type == 'TrueTypeUnicode') {
03272                 $enc = 'Identity-H';
03273             } else {
03274                 $this->Error('Unknow font type: '.$type.'');
03275             }
03276             $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg));
03277             if (isset($diff) AND (!empty($diff))) {
03278                 //Search existing encodings
03279                 $d = 0;
03280                 $nb = count($this->diffs);
03281                 for ($i=1; $i <= $nb; ++$i) {
03282                     if ($this->diffs[$i] == $diff) {
03283                         $d = $i;
03284                         break;
03285                     }
03286                 }
03287                 if ($d == 0) {
03288                     $d = $nb + 1;
03289                     $this->diffs[$d] = $diff;
03290                 }
03291                 $this->setFontSubBuffer($fontkey, 'diff', $d);
03292             }
03293             if (!$this->empty_string($file)) {
03294                 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
03295                     $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir);
03296                 } elseif ($type != 'core') {
03297                     $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir);
03298                 }
03299             }
03300             return $fontdata;
03301         }
03302 
03317         public function SetFont($family, $style='', $size=0, $fontfile='') {
03318             //Select a font; size given in points
03319             if ($size == 0) {
03320                 $size = $this->FontSizePt;
03321             }
03322             // try to add font (if not already added)
03323             $fontdata = $this->AddFont($family, $style, $fontfile);
03324             $this->FontFamily = $fontdata['family'];
03325             $this->FontStyle = $fontdata['style'];
03326             $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
03327             $this->SetFontSize($size);
03328         }
03329 
03337         public function SetFontSize($size) {
03338             //Set font size in points
03339             $this->FontSizePt = $size;
03340             $this->FontSize = $size / $this->k;
03341             if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
03342                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
03343             } else {
03344                 $this->FontAscent = 0.8 * $this->FontSize;
03345             }
03346             if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
03347                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
03348             } else {
03349                 $this->FontDescent = 0.2 * $this->FontSize;
03350             }
03351             if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
03352                 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
03353             }
03354         }
03355 
03362         public function SetDefaultMonospacedFont($font) {
03363             $this->default_monospaced_font = $font;
03364         }
03365         
03373         public function AddLink() {
03374             //Create a new internal link
03375             $n = count($this->links) + 1;
03376             $this->links[$n] = array(0, 0);
03377             return $n;
03378         }
03379 
03389         public function SetLink($link, $y=0, $page=-1) {
03390             if ($y == -1) {
03391                 $y = $this->y;
03392             }
03393             if ($page == -1) {
03394                 $page = $this->page;
03395             }
03396             $this->links[$link] = array($page, $y);
03397         }
03398 
03412         public function Link($x, $y, $w, $h, $link, $spaces=0) {
03413             $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
03414         }
03415         
03429         public function Annotation($x='', $y='', $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
03430             if ($x === '') {
03431                 $x = $this->x;
03432             }
03433             if ($y === '') {
03434                 $y = $this->y;
03435             }
03436             // recalculate coordinates to account for graphic transformations
03437             if (isset($this->transfmatrix)) {
03438                 for ($i=$this->transfmatrix_key; $i > 0; --$i) {
03439                     $maxid = count($this->transfmatrix[$i]) - 1;
03440                     for ($j=$maxid; $j >= 0; --$j) {
03441                         $ctm = $this->transfmatrix[$i][$j];
03442                         if (isset($ctm['a'])) {
03443                             $x = $x * $this->k;
03444                             $y = ($this->h - $y) * $this->k;
03445                             $w = $w * $this->k;
03446                             $h = $h * $this->k;
03447                             // top left
03448                             $xt = $x;
03449                             $yt = $y;
03450                             $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
03451                             $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
03452                             // top right
03453                             $xt = $x + $w;
03454                             $yt = $y;
03455                             $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
03456                             $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
03457                             // bottom left
03458                             $xt = $x;
03459                             $yt = $y - $h;
03460                             $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
03461                             $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
03462                             // bottom right
03463                             $xt = $x + $w;
03464                             $yt = $y - $h;
03465                             $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
03466                             $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
03467                             // new coordinates (rectangle area)
03468                             $x = min($x1, $x2, $x3, $x4);
03469                             $y = max($y1, $y2, $y3, $y4);
03470                             $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
03471                             $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
03472                             $x = $x / $this->k;
03473                             $y = $this->h - ($y / $this->k);
03474                         }
03475                     }
03476                 }
03477             }
03478             if ($this->page <= 0) {
03479                 $page = 1;
03480             } else {
03481                 $page = $this->page;
03482             }
03483             if (!isset($this->PageAnnots[$page])) {
03484                 $this->PageAnnots[$page] = array();
03485             }
03486             $this->PageAnnots[$page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
03487             if (($opt['Subtype'] == 'FileAttachment') AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
03488                 $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + $this->embedded_start_obj_id));
03489             }
03490             // Add widgets annotation's icons
03491             if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) {
03492                 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
03493             }
03494             if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) {
03495                 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
03496             }
03497             if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) {
03498                 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
03499             }
03500             ++$this->annot_obj_id;
03501         }
03502 
03509         protected function _putEmbeddedFiles() {
03510             reset($this->embeddedfiles);
03511             foreach ($this->embeddedfiles as $filename => $filedata) {
03512                 $data = file_get_contents($filedata['file']);
03513                 $filter = '';
03514                 if ($this->compress) {
03515                     $data = gzcompress($data);
03516                     $filter = ' /Filter /FlateDecode';
03517                 }
03518                 $this->offsets[$filedata['n']] = $this->bufferlen;
03519                 $this->_out($filedata['n'].' 0 obj');
03520                 $this->_out('<</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>');
03521                 $this->_putstream($data);
03522                 $this->_out('endobj');
03523             }
03524         }
03525         
03540         public function Text($x, $y, $txt, $stroke=0, $clip=false) {
03541             //Output a string
03542             if ($this->rtl) {
03543                 // bidirectional algorithm (some chars may be changed affecting the line length)
03544                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
03545                 $l = $this->GetArrStringWidth($s);
03546                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
03547             } else {
03548                 $xr = $x;
03549             }
03550             $opt = '';
03551             if (($stroke > 0) AND (!$clip)) {
03552                 $opt .= '1 Tr '.intval($stroke).' w ';
03553             } elseif (($stroke > 0) AND $clip) {
03554                 $opt .= '5 Tr '.intval($stroke).' w ';
03555             } elseif ($clip) {
03556                 $opt .= '7 Tr ';
03557             }
03558             $s = sprintf('BT %.2F %.2F Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
03559             if ($this->underline AND ($txt!='')) {
03560                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
03561             }
03562             if ($this->linethrough AND ($txt!='')) { 
03563                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt); 
03564             }
03565             if ($this->ColorFlag AND (!$clip)) {
03566                 $s='q '.$this->TextColor.' '.$s.' Q';
03567             }
03568             $this->_out($s);
03569         }
03570 
03580         public function AcceptPageBreak() {
03581             return $this->AutoPageBreak;
03582         }
03583         
03593         protected function checkPageBreak($h=0, $y='', $addpage=true) {
03594             if ($this->empty_string($y)) {
03595                 $y = $this->y;
03596             }
03597             if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
03598                 if ($addpage) {
03599                     //Automatic page break
03600                     $x = $this->x;
03601                     $this->AddPage($this->CurOrientation);
03602                     $this->y = $this->tMargin;
03603                     $oldpage = $this->page - 1;
03604                     if ($this->rtl) {
03605                         if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
03606                             $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
03607                         } else {
03608                             $this->x = $x;
03609                         }
03610                     } else {
03611                         if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
03612                             $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
03613                         } else {
03614                             $this->x = $x;
03615                         }
03616                     }
03617                 }
03618                 return true;
03619             }
03620             return false;
03621         }
03622 
03641         public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
03642             //$min_cell_height = $this->FontAscent + $this->FontDescent;
03643             $min_cell_height = $this->FontSize * $this->cell_height_ratio;
03644             if ($h < $min_cell_height) {
03645                 $h = $min_cell_height;
03646             }
03647             $this->checkPageBreak($h);
03648             $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height));
03649         }
03650 
03658         public function removeSHY($txt='') {
03659             /*
03660             * Unicode Data
03661             * Name : SOFT HYPHEN, commonly abbreviated as SHY
03662             * HTML Entity (decimal): &#173;
03663             * HTML Entity (hex): &#xad;
03664             * HTML Entity (named): &shy;
03665             * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
03666             * UTF-8 (hex): 0xC2 0xAD (c2ad)
03667             * UTF-8 character: chr(194).chr(173)
03668             */
03669             $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
03670             if (!$this->isunicode) {
03671                 $txt = preg_replace('/([\\xad]{1})/', '', $txt);
03672             }
03673             return $txt;
03674         }
03675         
03693         protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
03694             $txt = $this->removeSHY($txt);
03695             $rs = ''; //string to be returned
03696             if (!$ignore_min_height) {
03697                 $min_cell_height = $this->FontSize * $this->cell_height_ratio;
03698                 if ($h < $min_cell_height) {
03699                     $h = $min_cell_height;
03700                 }
03701             }
03702             $k = $this->k;
03703             if ($this->empty_string($w) OR ($w <= 0)) {
03704                 if ($this->rtl) {
03705                     $w = $this->x - $this->lMargin;
03706                 } else {
03707                     $w = $this->w - $this->rMargin - $this->x;
03708                 }
03709             }
03710             $s = '';            
03711             if (($fill == 1) OR ($border == 1)) {
03712                 if ($fill == 1) {
03713                     $op = ($border == 1) ? 'B' : 'f';
03714                 } else {
03715                     $op = 'S';
03716                 }
03717                 if ($this->rtl) {
03718                     $xk = (($this->x  - $w) * $k);
03719                 } else {
03720                     $xk = ($this->x * $k);
03721                 }
03722                 $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
03723             }
03724             if (is_string($border)) {
03725                 $lm = ($this->LineWidth / 2);
03726                 $x = $this->x;
03727                 $y = $this->y;
03728                 if (strpos($border,'L') !== false) {
03729                     if ($this->rtl) {
03730                         $xk = ($x - $w) * $k;
03731                     } else {
03732                         $xk = $x * $k;
03733                     }
03734                     $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k));
03735                 }
03736                 if (strpos($border,'T') !== false) {
03737                     if ($this->rtl) {
03738                         $xk = ($x - $w + $lm) * $k;
03739                         $xwk = ($x - $lm) * $k;
03740                     } else {
03741                         $xk = ($x - $lm) * $k;
03742                         $xwk = ($x + $w + $lm) * $k;
03743                     }
03744                     $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
03745                 }
03746                 if (strpos($border,'R') !== false) {
03747                     if ($this->rtl) {
03748                         $xk = $x * $k;
03749                     } else {
03750                         $xk = ($x + $w) * $k;
03751                     }
03752                     $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k));
03753                 }
03754                 if (strpos($border,'B') !== false) {
03755                     if ($this->rtl) {
03756                         $xk = ($x - $w + $lm) * $k;
03757                         $xwk = ($x - $lm) * $k;
03758                     } else {
03759                         $xk = ($x - $lm) * $k;
03760                         $xwk = ($x + $w + $lm) * $k;
03761                     }
03762                     $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
03763                 }
03764             }
03765             if ($txt != '') {
03766                 // text lenght
03767                 $width = $this->GetStringWidth($txt);
03768                 // ratio between cell lenght and text lenght
03769                 if ($width <= 0) {
03770                     $ratio = 1;
03771                 } else {
03772                     $ratio = ($w - (2 * $this->cMargin)) / $width;
03773                 }
03774                 // stretch text if required
03775                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
03776                     if ($stretch > 2) {
03777                         // spacing
03778                         //Calculate character spacing in points
03779                         $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
03780                         //Set character spacing
03781                         $rs .= sprintf('BT %.2F Tc ET ', $char_space);
03782                     } else {
03783                         // scaling
03784                         //Calculate horizontal scaling
03785                         $horiz_scale = $ratio * 100.0;
03786                         //Set horizontal scaling
03787                         $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale);
03788                     }
03789                     $align = '';
03790                     $width = $w - (2 * $this->cMargin);
03791                 } else {
03792                     $stretch == 0;
03793                 }
03794                 if ($align == 'L') {
03795                     if ($this->rtl) {
03796                         $dx = $w - $width - $this->cMargin;
03797                     } else {
03798                         $dx = $this->cMargin;
03799                     }
03800                 } elseif ($align == 'R') {
03801                     if ($this->rtl) {
03802                         $dx = $this->cMargin;
03803                     } else {
03804                         $dx = $w - $width - $this->cMargin;
03805                     }
03806                 } elseif ($align == 'C') {
03807                     $dx = ($w - $width) / 2;
03808                 } elseif ($align == 'J') {
03809                     if ($this->rtl) {
03810                         $dx = $w - $width - $this->cMargin;
03811                     } else {
03812                         $dx = $this->cMargin;
03813                     }
03814                 } else {
03815                     $dx = $this->cMargin;
03816                 }
03817                 if ($this->ColorFlag) {
03818                     $s .= 'q '.$this->TextColor.' ';
03819                 }
03820                 $txt2 = $this->_escapetext($txt);
03821                 if ($this->rtl) {
03822                     $xdk = ($this->x - $dx - $width) * $k;
03823                 } else {
03824                     $xdk = ($this->x + $dx) * $k;
03825                 }
03826                 // Justification
03827                 if ($align == 'J') {
03828                     // count number of spaces
03829                     $ns = substr_count($txt, ' ');
03830                     if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
03831                         // get string width without spaces
03832                         $width = $this->GetStringWidth(str_replace(' ', '', $txt));
03833                         // calculate average space width
03834                         $spacewidth = -1000 * ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize;
03835                         // set word position to be used with TJ operator
03836                         $txt2 = str_replace(chr(0).' ', ') '.($spacewidth).' (', $txt2);
03837                     } else {
03838                         // get string width
03839                         $width = $this->GetStringWidth($txt);
03840                         $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
03841                         $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
03842                     }
03843                 }
03844                 // calculate approximate position of the font base line
03845                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
03846                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
03847                 // print text
03848                 $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
03849                 if ($this->rtl) {
03850                     $xdx = $this->x - $dx - $width;
03851                 } else {
03852                     $xdx = $this->x + $dx;
03853                 }
03854                 if ($this->underline)  {
03855                     $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
03856                 }
03857                 if ($this->linethrough) { 
03858                     $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
03859                 }
03860                 if ($this->ColorFlag) {
03861                     $s .= ' Q';
03862                 }
03863                 if ($link) {
03864                     $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, substr_count($txt, chr(32)));
03865                 }
03866             }
03867             // output cell
03868             if ($s) {
03869                 // output cell
03870                 $rs .= $s;
03871                 // reset text stretching
03872                 if ($stretch > 2) {
03873                     //Reset character horizontal spacing
03874                     $rs .= ' BT 0 Tc ET';
03875                 } elseif ($stretch > 0) {
03876                     //Reset character horizontal scaling
03877                     $rs .= ' BT 100 Tz ET';
03878                 }
03879             }
03880             // reset word spacing
03881             if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) {
03882                 $rs .= ' BT 0 Tw ET';
03883             }
03884             $this->lasth = $h;
03885             if ($ln > 0) {
03886                 //Go to the beginning of the next line
03887                 $this->y += $h;
03888                 if ($ln == 1) {
03889                     if ($this->rtl) {
03890                         $this->x = $this->w - $this->rMargin;
03891                     } else {
03892                         $this->x = $this->lMargin;
03893                     }
03894                 }
03895             } else {
03896                 // go left or right by case
03897                 if ($this->rtl) {
03898                     $this->x -= $w;
03899                 } else {
03900                     $this->x += $w;
03901                 }
03902             }
03903             $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
03904             $rs = $gstyles.$rs;
03905             return $rs;
03906         }
03907 
03931         public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0) { 
03932             if ($this->empty_string($this->lasth) OR $reseth) {
03933                 //set row height
03934                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
03935             }
03936             if (!$this->empty_string($y)) {
03937                 $this->SetY($y);
03938             } else {
03939                 $y = $this->GetY();
03940             }
03941             // check for page break
03942             $this->checkPageBreak($h);
03943             $y = $this->GetY();
03944             // get current page number
03945             $startpage = $this->page;
03946             if (!$this->empty_string($x)) {
03947                 $this->SetX($x);
03948             } else {
03949                 $x = $this->GetX();
03950             }
03951             if ($this->empty_string($w) OR ($w <= 0)) {
03952                 if ($this->rtl) {
03953                     $w = $this->x - $this->lMargin;
03954                 } else {
03955                     $w = $this->w - $this->rMargin - $this->x;
03956                 }
03957             }
03958             // store original margin values
03959             $lMargin = $this->lMargin;
03960             $rMargin = $this->rMargin;
03961             if ($this->rtl) {
03962                 $this->SetRightMargin($this->w - $this->x);
03963                 $this->SetLeftMargin($this->x - $w);
03964             } else {
03965                 $this->SetLeftMargin($this->x);
03966                 $this->SetRightMargin($this->w - $this->x - $w);
03967             }
03968             $starty = $this->y;
03969             if ($autopadding) {
03970                 // Adjust internal padding
03971                 if ($this->cMargin < ($this->LineWidth / 2)) {
03972                     $this->cMargin = ($this->LineWidth / 2);
03973                 }
03974                 // Add top space if needed
03975                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
03976                     $this->y += $this->LineWidth / 2;
03977                 }
03978                 // add top padding
03979                 $this->y += $this->cMargin;
03980             }
03981             if ($ishtml) {
03982                 // ******* Write HTML text
03983                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
03984                 $nl = 1;
03985             } else {
03986                 // ******* Write text
03987                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, false, $maxh);
03988             }
03989             if ($autopadding) {
03990                 // add bottom padding
03991                 $this->y += $this->cMargin;
03992                 // Add bottom space if needed
03993                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
03994                     $this->y += $this->LineWidth / 2;
03995                 }
03996             }
03997             // Get end-of-text Y position
03998             $currentY = $this->y;
03999             // get latest page number
04000             $endpage = $this->page;
04001             // check if a new page has been created
04002             if ($endpage > $startpage) {
04003                 // design borders around HTML cells.
04004                 for ($page=$startpage; $page <= $endpage; ++$page) {
04005                     $this->setPage($page);
04006                     if ($page == $startpage) {
04007                         $this->y = $starty; // put cursor at the beginning of cell on the first page
04008                         $h = $this->getPageHeight() - $starty - $this->getBreakMargin();
04009                         $cborder = $this->getBorderMode($border, $position='start');
04010                     } elseif ($page == $endpage) {
04011                         $this->y = $this->tMargin; // put cursor at the beginning of last page
04012                         $h = $currentY - $this->tMargin;
04013                         $cborder = $this->getBorderMode($border, $position='end');
04014                     } else {
04015                         $this->y = $this->tMargin; // put cursor at the beginning of the current page
04016                         $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
04017                         $cborder = $this->getBorderMode($border, $position='middle');
04018                     }
04019                     $nx = $x;
04020                     // account for margin changes
04021                     if ($page > $startpage) {
04022                         if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
04023                             $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
04024                         } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
04025                             $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
04026                         }
04027                     }
04028                     $this->SetX($nx);
04029                     $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false);
04030                     if ($cborder OR $fill) {
04031                         $pagebuff = $this->getPageBuffer($this->page);
04032                         $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
04033                         $pend = substr($pagebuff, $this->intmrk[$this->page]);
04034                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
04035                         $this->intmrk[$this->page] += strlen($ccode."\n");
04036                     }
04037                 }
04038             } else {
04039                 $h = max($h, ($currentY - $y));
04040                 // put cursor at the beginning of text
04041                 $this->SetY($y); 
04042                 $this->SetX($x);
04043                 // design a cell around the text
04044                 $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true);
04045                 if ($border OR $fill) {
04046                     if (end($this->transfmrk[$this->page]) !== false) {
04047                         $pagemarkkey = key($this->transfmrk[$this->page]);
04048                         $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
04049                     } elseif ($this->InFooter) {
04050                         $pagemark = &$this->footerpos[$this->page];
04051                     } else {
04052                         $pagemark = &$this->intmrk[$this->page];
04053                     }
04054                     $pagebuff = $this->getPageBuffer($this->page);
04055                     $pstart = substr($pagebuff, 0, $pagemark);
04056                     $pend = substr($pagebuff, $pagemark);
04057                     $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
04058                     $pagemark += strlen($ccode."\n");
04059                 }
04060             }
04061             // Get end-of-cell Y position
04062             $currentY = $this->GetY();
04063             // restore original margin values
04064             $this->SetLeftMargin($lMargin);
04065             $this->SetRightMargin($rMargin);
04066             if ($ln > 0) {
04067                 //Go to the beginning of the next line
04068                 $this->SetY($currentY);
04069                 if ($ln == 2) {
04070                     $this->SetX($x + $w);
04071                 }
04072             } else {
04073                 // go left or right by case
04074                 $this->setPage($startpage);
04075                 $this->y = $y;
04076                 $this->SetX($x + $w);
04077             }
04078             $this->setContentMark();
04079             return $nl;
04080         }
04081 
04090         protected function getBorderMode($border, $position='start') {
04091             if ((!$this->opencell) AND ($border == 1)) {
04092                 return 1;
04093             }
04094             $cborder = '';
04095             switch ($position) {
04096                 case 'start': {
04097                     if ($border == 1) {
04098                         $cborder = 'LTR';
04099                     } else {
04100                         if (!(false === strpos($border, 'L'))) {
04101                             $cborder .= 'L';
04102                         }
04103                         if (!(false === strpos($border, 'T'))) {
04104                             $cborder .= 'T';
04105                         }
04106                         if (!(false === strpos($border, 'R'))) {
04107                             $cborder .= 'R';
04108                         }
04109                         if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
04110                             $cborder .= 'B';
04111                         }
04112                     }
04113                     break;
04114                 }
04115                 case 'middle': {
04116                     if ($border == 1) {
04117                         $cborder = 'LR';
04118                     } else {
04119                         if (!(false === strpos($border, 'L'))) {
04120                             $cborder .= 'L';
04121                         }
04122                         if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
04123                             $cborder .= 'T';
04124                         }
04125                         if (!(false === strpos($border, 'R'))) {
04126                             $cborder .= 'R';
04127                         }
04128                         if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
04129                             $cborder .= 'B';
04130                         }
04131                     }
04132                     break;
04133                 }
04134                 case 'end': {
04135                     if ($border == 1) {
04136                         $cborder = 'LRB';
04137                     } else {
04138                         if (!(false === strpos($border, 'L'))) {
04139                             $cborder .= 'L';
04140                         }
04141                         if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
04142                             $cborder .= 'T';
04143                         }
04144                         if (!(false === strpos($border, 'R'))) {
04145                             $cborder .= 'R';
04146                         }
04147                         if (!(false === strpos($border, 'B'))) {
04148                             $cborder .= 'B';
04149                         }
04150                     }
04151                     break;
04152                 }
04153                 default: {
04154                     $cborder = $border;
04155                     break;
04156                 }
04157             }
04158             return $cborder;
04159         }
04160 
04169         public function getNumLines($txt, $w=0) {
04170             $lines = 0;
04171             if ($this->empty_string($w) OR ($w <= 0)) {
04172                 if ($this->rtl) {
04173                     $w = $this->x - $this->lMargin;
04174                 } else {
04175                     $w = $this->w - $this->rMargin - $this->x;
04176                 }
04177             }
04178             // max column width
04179             $wmax = $w - (2 * $this->cMargin);
04180             // remove carriage returns
04181             $txt = str_replace("\r", '', $txt);
04182             // remove last newline (if any)
04183             if (substr($txt,-1) == "\n") {
04184                 $txt = substr($txt, 0, -1);
04185             }
04186             // divide text in blocks
04187             $txtblocks = explode("\n", $txt);
04188             // for each block;
04189             foreach ($txtblocks as $block) {
04190                 // estimate the number of lines
04191                 $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax));
04192             }
04193             return $lines;
04194         }
04195             
04212         public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) {
04213             if (strlen($txt) == 0) {
04214                 $txt = ' ';
04215             }
04216             // remove carriage returns
04217             $s = str_replace("\r", '', $txt);
04218             // check if string contains arabic text
04219             if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
04220                 $arabic = true;
04221             } else {
04222                 $arabic = false;
04223             }
04224             // check if string contains RTL text
04225             if ($arabic OR $this->tmprtl OR preg_match(K_RE_PATTERN_RTL, $txt)) {
04226                 $rtlmode = true;
04227             } else {
04228                 $rtlmode = false;
04229             }
04230             // get a char width
04231             $chrwidth = $this->GetCharWidth('.');
04232             // get array of unicode values
04233             $chars = $this->UTF8StringToArray($s);
04234             // get array of chars
04235             $uchars = $this->UTF8ArrayToUniArray($chars);
04236             // get the number of characters
04237             $nb = count($chars);
04238             // replacement for SHY character (minus symbol)
04239             $shy_replacement = 45;
04240             $shy_replacement_char = $this->unichr($shy_replacement);
04241             // widht for SHY replacement
04242             $shy_replacement_width = $this->GetCharWidth($shy_replacement);
04243             // store current position
04244             $prevx = $this->x;
04245             $prevy = $this->y;
04246             // max Y
04247             $maxy = $this->y + $maxh - $h - (2 * $this->cMargin);
04248             // calculate remaining line width ($w)
04249             if ($this->rtl) {
04250                 $w = $this->x - $this->lMargin;
04251             } else {
04252                 $w = $this->w - $this->rMargin - $this->x;
04253             }
04254             // max column width
04255             $wmax = $w - (2 * $this->cMargin);
04256             if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) {
04257                 // a single character do not fit on column
04258                 return '';
04259             }
04260             $i = 0; // character position
04261             $j = 0; // current starting position
04262             $sep = -1; // position of the last blank space
04263             $shy = false; // true if the last blank is a soft hypen (SHY)
04264             $l = 0; // current string lenght
04265             $nl = 0; //number of lines
04266             $linebreak = false;
04267             // for each character
04268             while ($i < $nb) {
04269                 if (($maxh > 0) AND ($this->y >= $maxy) ) {
04270                     $firstline = true;
04271                 }
04272                 //Get the current character
04273                 $c = $chars[$i];
04274                 if ($c == 10) { // 10 = "\n" = new line
04275                     //Explicit line break
04276                     if ($align == 'J') {
04277                         if ($this->rtl) {
04278                             $talign = 'R';
04279                         } else {
04280                             $talign = 'L';
04281                         }
04282                     } else {
04283                         $talign = $align;
04284                     }
04285                     $tmpstr = $this->UniArrSubString($uchars, $j, $i);
04286                     if ($firstline) {
04287                         $startx = $this->x;
04288                         $tmparr = array_slice($chars, $j, $i);
04289                         if ($rtlmode) {
04290                             $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
04291                         }
04292                         $linew = $this->GetArrStringWidth($tmparr);
04293                         unset($tmparr);
04294                         if ($this->rtl) {
04295                             $this->endlinex = $startx - $linew;
04296                         } else {
04297                             $this->endlinex = $startx + $linew;
04298                         }
04299                         $w = $linew;
04300                         $tmpcmargin = $this->cMargin;
04301                         if ($maxh == 0) {
04302                             $this->cMargin = 0;
04303                         }
04304                     }
04305                     $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
04306                     unset($tmpstr);
04307                     if ($firstline) {
04308                         $this->cMargin = $tmpcmargin;
04309                         return ($this->UniArrSubString($uchars, $i));
04310                     }
04311                     ++$nl;
04312                     $j = $i + 1;
04313                     $l = 0;
04314                     $sep = -1;
04315                     $shy = false;
04316                     // account for margin changes
04317                     if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
04318                         // AcceptPageBreak() may be overriden on extended classed to include margin changes
04319                         $this->AcceptPageBreak();
04320                     }
04321                     $w = $this->getRemainingWidth();
04322                     $wmax = $w - (2 * $this->cMargin);
04323                 } else {
04324                     // 160 is the non-breaking space.
04325                     // 173 is SHY (Soft Hypen).
04326                     // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
04327                     // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
04328                     // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
04329                     if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
04330                         // update last blank space position
04331                         $sep = $i;
04332                         // check if is a SHY
04333                         if ($c == 173) {
04334                             $shy = true;
04335                         } else {
04336                             $shy = false;
04337                         }
04338                     }
04339                     // update string length
04340                     if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) {
04341                         // with bidirectional algorithm some chars may be changed affecting the line length
04342                         // *** very slow ***
04343                         $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), '', $this->tmprtl));
04344                     } else {
04345                         $l += $this->GetCharWidth($c);
04346                     }
04347                     if (($l > $wmax) OR ($shy AND (($l + $shy_replacement_width) > $wmax)) ) {
04348                         // we have reached the end of column
04349                         if ($sep == -1) {
04350                             // check if the line was already started
04351                             if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
04352                                 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
04353                                 // print a void cell and go to next line
04354                                 $this->Cell($w, $h, '', 0, 1);
04355                                 $linebreak = true;
04356                                 if ($firstline) {
04357                                     return ($this->UniArrSubString($uchars, $j));
04358                                 }
04359                             } else {
04360                                 // truncate the word because do not fit on column
04361                                 $tmpstr = $this->UniArrSubString($uchars, $j, $i);
04362                                 if ($firstline) {
04363                                     $startx = $this->x;
04364                                     $tmparr = array_slice($chars, $j, $i);
04365                                     if ($rtlmode) {
04366                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
04367                                     }
04368                                     $linew = $this->GetArrStringWidth($tmparr);
04369                                     unset($tmparr);
04370                                     if ($this->rtl) {
04371                                         $this->endlinex = $startx - $linew;
04372                                     } else {
04373                                         $this->endlinex = $startx + $linew;
04374                                     }
04375                                     $w = $linew;
04376                                     $tmpcmargin = $this->cMargin;
04377                                     if ($maxh == 0) {
04378                                         $this->cMargin = 0;
04379                                     }
04380                                 }
04381                                 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
04382                                 unset($tmpstr);
04383                                 if ($firstline) {
04384                                     $this->cMargin = $tmpcmargin;
04385                                     return ($this->UniArrSubString($uchars, $i));
04386                                 }
04387                                 $j = $i;
04388                                 --$i;
04389                             }   
04390                         } else {
04391                             // word wrapping
04392                             if ($this->rtl AND (!$firstblock)) {
04393                                 $endspace = 1;
04394                             } else {
04395                                 $endspace = 0;
04396                             }
04397                             if ($shy) {
04398                                 // add hypen (minus symbol) at the end of the line
04399                                 $shy_width = $shy_replacement_width;
04400                                 if ($this->rtl) {
04401                                     $shy_char_left = $shy_replacement_char;
04402                                     $shy_char_right = '';
04403                                 } else {
04404                                     $shy_char_left = '';
04405                                     $shy_char_right = $shy_replacement_char;
04406                                 }
04407                             } else {
04408                                 $shy_width = 0;
04409                                 $shy_char_left = '';
04410                                 $shy_char_right = '';
04411                             }
04412                             $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
04413                             if ($firstline) {
04414                                 $startx = $this->x;
04415                                 $tmparr = array_slice($chars, $j, ($sep + $endspace));
04416                                 if ($rtlmode) {
04417                                     $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
04418                                 }
04419                                 $linew = $this->GetArrStringWidth($tmparr);
04420                                 unset($tmparr);
04421                                 if ($this->rtl) {
04422                                     $this->endlinex = $startx - $linew - $shy_width;
04423                                 } else {
04424                                     $this->endlinex = $startx + $linew + $shy_width;
04425                                 }
04426                                 $w = $linew;
04427                                 $tmpcmargin = $this->cMargin;
04428                                 if ($maxh == 0) {
04429                                     $this->cMargin = 0;
04430                                 }
04431                             }
04432                             // print the line
04433                             $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
04434                             unset($tmpstr);
04435                             if ($firstline) {
04436                                 // return the remaining text
04437                                 $this->cMargin = $tmpcmargin;
04438                                 return ($this->UniArrSubString($uchars, ($sep + $endspace)));
04439                             }
04440                             $i = $sep;
04441                             $sep = -1;
04442                             $shy = false;
04443                             $j = ($i+1);
04444                         }
04445                         // account for margin changes
04446                         if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
04447                             // AcceptPageBreak() may be overriden on extended classed to include margin changes
04448                             $this->AcceptPageBreak();
04449                         }
04450                         $w = $this->getRemainingWidth();
04451                         $wmax = $w - (2 * $this->cMargin);
04452                         if ($linebreak) {
04453                             $linebreak = false;
04454                         } else {
04455                             ++$nl;
04456                             $l = 0;
04457                         }
04458                     }
04459                 }
04460                 ++$i;
04461             } // end while i < nb
04462             // print last substring (if any)
04463             if ($l > 0) {
04464                 switch ($align) {
04465                     case 'J':
04466                     case 'C': {
04467                         $w = $w;
04468                         break;
04469                     }
04470                     case 'L': {
04471                         if ($this->rtl) {
04472                             $w = $w;
04473                         } else {
04474                             $w = $l;
04475                         }
04476                         break;
04477                     }
04478                     case 'R': {
04479                         if ($this->rtl) {
04480                             $w = $l;
04481                         } else {
04482                             $w = $w;
04483                         }
04484                         break;
04485                     }
04486                     default: {
04487                         $w = $l;
04488                         break;
04489                     }
04490                 }
04491                 $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
04492                 if ($firstline) {
04493                     $startx = $this->x;
04494                     $tmparr = array_slice($chars, $j, $nb);
04495                     if ($rtlmode) {
04496                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
04497                     }
04498                     $linew = $this->GetArrStringWidth($tmparr);
04499                     unset($tmparr);
04500                     if ($this->rtl) {
04501                         $this->endlinex = $startx - $linew;
04502                     } else {
04503                         $this->endlinex = $startx + $linew;
04504                     }
04505                     $w = $linew;
04506                     $tmpcmargin = $this->cMargin;
04507                     if ($maxh == 0) {
04508                         $this->cMargin = 0;
04509                     }
04510                 }
04511                 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
04512                 unset($tmpstr);
04513                 if ($firstline) {
04514                     $this->cMargin = $tmpcmargin;
04515                     return ($this->UniArrSubString($uchars, $nb));
04516                 }
04517                 ++$nl;
04518             }
04519             if ($firstline) {
04520                 return '';
04521             }
04522             return $nl;
04523         }
04524                 
04530         protected function getRemainingWidth() {
04531             if ($this->rtl) {
04532                 return ($this->x - $this->lMargin);
04533             } else {
04534                 return ($this->w - $this->rMargin - $this->x);
04535             }
04536         }
04537 
04546         public function UTF8ArrSubString($strarr, $start='', $end='') {
04547             if (strlen($start) == 0) {
04548                 $start = 0;
04549             }
04550             if (strlen($end) == 0) {
04551                 $end = count($strarr);
04552             }
04553             $string = '';
04554             for ($i=$start; $i < $end; ++$i) {
04555                 $string .= $this->unichr($strarr[$i]);
04556             }
04557             return $string;
04558         }
04559 
04569         public function UniArrSubString($uniarr, $start='', $end='') {
04570             if (strlen($start) == 0) {
04571                 $start = 0;
04572             }
04573             if (strlen($end) == 0) {
04574                 $end = count($uniarr);
04575             }
04576             $string = '';
04577             for ($i=$start; $i < $end; ++$i) {
04578                 $string .= $uniarr[$i];
04579             }
04580             return $string;
04581         }
04582 
04590         public function UTF8ArrayToUniArray($ta) {
04591             return array_map(array($this, 'unichr'), $ta);
04592         }
04593         
04602         public function unichr($c) {
04603             if (!$this->isunicode) {
04604                 return chr($c);
04605             } elseif ($c <= 0x7F) {
04606                 // one byte
04607                 return chr($c);
04608             } elseif ($c <= 0x7FF) {
04609                 // two bytes
04610                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
04611             } elseif ($c <= 0xFFFF) {
04612                 // three bytes
04613                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
04614             } elseif ($c <= 0x10FFFF) {
04615                 // four bytes
04616                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
04617             } else {
04618                 return '';
04619             }
04620         }
04621         
04653         public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false) {
04654             if ($x === '') {
04655                 $x = $this->x;
04656             }
04657             if ($y === '') {
04658                 $y = $this->y;
04659             }
04660             // get image dimensions
04661             $imsize = @getimagesize($file);
04662             if ($imsize === FALSE) {
04663                 // encode spaces on filename
04664                 $file = str_replace(' ', '%20', $file);
04665                 $imsize = @getimagesize($file);
04666                 if ($imsize === FALSE) {
04667                     $this->Error('[Image] No such file or directory in '.$file);
04668                 }
04669             }
04670             // get original image width and height in pixels
04671             list($pixw, $pixh) = $imsize;
04672             // calculate image width and height on document
04673             if (($w <= 0) AND ($h <= 0)) {
04674                 // convert image size to document unit
04675                 $w = $this->pixelsToUnits($pixw);
04676                 $h = $this->pixelsToUnits($pixh);
04677             } elseif ($w <= 0) {
04678                 $w = $h * $pixw / $pixh;
04679             } elseif ($h <= 0) {
04680                 $h = $w * $pixh / $pixw;
04681             } elseif ($fitbox AND ($w > 0) AND ($h > 0)) {
04682                 // scale image dimensions proportionally to fit within the ($w, $h) box
04683                 if ((($w * $pixh) / ($h * $pixw)) < 1) {
04684                     $h = $w * $pixh / $pixw;
04685                 } else {
04686                     $w = $h * $pixw / $pixh;
04687                 }
04688             }
04689             // calculate new minimum dimensions in pixels
04690             $neww = round($w * $this->k * $dpi / $this->dpi);
04691             $newh = round($h * $this->k * $dpi / $this->dpi);
04692             // check if resize is necessary (resize is used only to reduce the image)
04693             if (($neww * $newh) >= ($pixw * $pixh)) {
04694                 $resize = false;
04695             }
04696             // check if image has been already added on document
04697             if (!in_array($file, $this->imagekeys)) {
04698                 //First use of image, get info
04699                 if ($type == '') {
04700                     $fileinfo = pathinfo($file);
04701                     if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
04702                         $type = $fileinfo['extension'];
04703                     } else {
04704                         $this->Error('Image file has no extension and no type was specified: '.$file);
04705                     }
04706                 }
04707                 $type = strtolower($type);
04708                 if ($type == 'jpg') {
04709                     $type = 'jpeg';
04710                 }
04711                 $mqr = $this->get_mqr();
04712                 $this->set_mqr(false);
04713                 // Specific image handlers
04714                 $mtd = '_parse'.$type;
04715                 // GD image handler function
04716                 $gdfunction = 'imagecreatefrom'.$type;
04717                 $info = false;
04718                 if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
04719                     // TCPDF image functions
04720                     $info = $this->$mtd($file);
04721                     if ($info == 'pngalpha') {
04722                         return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
04723                     }
04724                 } 
04725                 if (!$info) {
04726                     if (function_exists($gdfunction)) {
04727                         // GD library
04728                         $img = $gdfunction($file);
04729                         if ($resize) {
04730                             $imgr = imagecreatetruecolor($neww, $newh);
04731                             imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 
04732                             $info = $this->_toJPEG($imgr);
04733                         } else {
04734                             $info = $this->_toJPEG($img);
04735                         }
04736                     } elseif (extension_loaded('imagick')) {
04737                         // ImageMagick library
04738                         $img = new Imagick();
04739                         $img->readImage($file);
04740                         if ($resize) {
04741                             $img->resizeImage($neww, $newh, 10, 1, false);
04742                         }
04743                         $img->setCompressionQuality($this->jpeg_quality);
04744                         $img->setImageFormat('jpeg');
04745                         $tempname = tempnam(K_PATH_CACHE, 'jpg_');
04746                         $img->writeImage($tempname);
04747                         $info = $this->_parsejpeg($tempname);
04748                         unlink($tempname);
04749                         $img->destroy();
04750                     } else {
04751                         return;
04752                     }
04753                 }
04754                 if ($info === false) {
04755                     //If false, we cannot process image
04756                     return;
04757                 }
04758                 $this->set_mqr($mqr);
04759                 if ($ismask) {
04760                     // force grayscale
04761                     $info['cs'] = 'DeviceGray';
04762                 }
04763                 $info['i'] = $this->numimages + 1;
04764                 if ($imgmask !== false) {
04765                     $info['masked'] = $imgmask;
04766                 }
04767                 // add image to document
04768                 $this->setImageBuffer($file, $info);
04769             } else {
04770                 $info = $this->getImageBuffer($file);
04771             }
04772             // Check whether we need a new page first as this does not fit
04773             if ($this->checkPageBreak($h, $y)) {
04774                 $y = $this->GetY() + $this->cMargin;
04775             }
04776             // set bottomcoordinates
04777             $this->img_rb_y = $y + $h;
04778             // set alignment
04779             if ($this->rtl) {
04780                 if ($palign == 'L') {
04781                     $ximg = $this->lMargin;
04782                     // set right side coordinate
04783                     $this->img_rb_x = $ximg + $w;
04784                 } elseif ($palign == 'C') {
04785                     $ximg = ($this->w - $x - $w) / 2;
04786                     // set right side coordinate
04787                     $this->img_rb_x = $ximg + $w;
04788                 } else {
04789                     $ximg = $this->w - $x - $w;
04790                     // set left side coordinate
04791                     $this->img_rb_x = $ximg;
04792                 }
04793             } else {
04794                 if ($palign == 'R') {
04795                     $ximg = $this->w - $this->rMargin - $w;
04796                     // set left side coordinate
04797                     $this->img_rb_x = $ximg;
04798                 } elseif ($palign == 'C') {
04799                     $ximg = ($this->w - $x - $w) / 2;
04800                     // set right side coordinate
04801                     $this->img_rb_x = $ximg + $w;
04802                 } else {
04803                     $ximg = $x;
04804                     // set right side coordinate
04805                     $this->img_rb_x = $ximg + $w;
04806                 }
04807             }
04808             if ($ismask OR $hidden) {
04809                 // image is not displayed
04810                 return $info['i'];
04811             }
04812             $xkimg = $ximg * $this->k;
04813             $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']));
04814             if (!empty($border)) {
04815                 $bx = $x;
04816                 $by = $y;
04817                 $this->x = $ximg;
04818                 $this->y = $y;
04819                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
04820                 $this->x = $bx;
04821                 $this->y = $by;
04822             }
04823             if ($link) {
04824                 $this->Link($ximg, $y, $w, $h, $link, 0);
04825             }
04826             // set pointer to align the successive text/objects
04827             switch($align) {
04828                 case 'T': {
04829                     $this->y = $y;
04830                     $this->x = $this->img_rb_x;
04831                     break;
04832                 }
04833                 case 'M': {
04834                     $this->y = $y + round($h/2);
04835                     $this->x = $this->img_rb_x;
04836                     break;
04837                 }
04838                 case 'B': {
04839                     $this->y = $this->img_rb_y;
04840                     $this->x = $this->img_rb_x;
04841                     break;
04842                 }
04843                 case 'N': {
04844                     $this->SetY($this->img_rb_y);
04845                     break;
04846                 }
04847                 default:{
04848                     break;
04849                 }
04850             }
04851             $this->endlinex = $this->img_rb_x;
04852             return $info['i'];
04853         }
04854         
04860         public function set_mqr($mqr) {
04861             if (function_exists('set_magic_quotes_runtime')) {
04862                 @set_magic_quotes_runtime($mqr);
04863             }
04864         }
04865         
04871         public function get_mqr() {
04872             if (function_exists('get_magic_quotes_runtime')) {
04873                 return @get_magic_quotes_runtime();
04874             }
04875             return 0;
04876         }
04877                         
04886         protected function _toJPEG($image) {
04887             $tempname = tempnam(K_PATH_CACHE, 'jpg_');
04888             imagejpeg($image, $tempname, $this->jpeg_quality);
04889             imagedestroy($image);
04890             $retvars = $this->_parsejpeg($tempname);
04891             // tidy up by removing temporary image
04892             unlink($tempname);
04893             return $retvars;
04894         }
04895         
04902         protected function _parsejpeg($file) {
04903             $a = getimagesize($file);
04904             if (empty($a)) {
04905                 $this->Error('Missing or incorrect image file: '.$file);
04906             }
04907             if ($a[2] != 2) {
04908                 $this->Error('Not a JPEG file: '.$file);
04909             }
04910             if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
04911                 $colspace = 'DeviceRGB';
04912             } elseif ($a['channels'] == 4) {
04913                 $colspace = 'DeviceCMYK';
04914             } else {
04915                 $colspace = 'DeviceGray';
04916             }
04917             $bpc = isset($a['bits']) ? $a['bits'] : 8;
04918             $data = file_get_contents($file);
04919             return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
04920         }
04921 
04928         protected function _parsepng($file) {
04929             $f = fopen($file, 'rb');
04930             if ($f === false) {
04931                 $this->Error('Can\'t open image file: '.$file);
04932             }
04933             //Check signature
04934             if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
04935                 $this->Error('Not a PNG file: '.$file);
04936             }
04937             //Read header chunk
04938             fread($f, 4);
04939             if (fread($f, 4) != 'IHDR') {
04940                 $this->Error('Incorrect PNG file: '.$file);
04941             }
04942             $w = $this->_freadint($f);
04943             $h = $this->_freadint($f);
04944             $bpc = ord(fread($f, 1));
04945             if ($bpc > 8) {
04946                 //$this->Error('16-bit depth not supported: '.$file);
04947                 fclose($f);
04948                 return false;
04949             }
04950             $ct = ord(fread($f, 1));
04951             if ($ct == 0) {
04952                 $colspace = 'DeviceGray';
04953             } elseif ($ct == 2) {
04954                 $colspace = 'DeviceRGB';
04955             } elseif ($ct == 3) {
04956                 $colspace = 'Indexed';
04957             } else {
04958                 // alpha channel
04959                 fclose($f);
04960                 return 'pngalpha';
04961             }
04962             if (ord(fread($f, 1)) != 0) {
04963                 //$this->Error('Unknown compression method: '.$file);
04964                 fclose($f);
04965                 return false;
04966             }
04967             if (ord(fread($f, 1)) != 0) {
04968                 //$this->Error('Unknown filter method: '.$file);
04969                 fclose($f);
04970                 return false;
04971             }
04972             if (ord(fread($f, 1)) != 0) {
04973                 //$this->Error('Interlacing not supported: '.$file);
04974                 fclose($f);
04975                 return false;
04976             }
04977             fread($f, 4);
04978             $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
04979             //Scan chunks looking for palette, transparency and image data
04980             $pal = '';
04981             $trns = '';
04982             $data = '';
04983             do {
04984                 $n = $this->_freadint($f);
04985                 $type = fread($f, 4);
04986                 if ($type == 'PLTE') {
04987                     //Read palette
04988                     $pal = $this->rfread($f, $n);
04989                     fread($f, 4);
04990                 } elseif ($type == 'tRNS') {
04991                     //Read transparency info
04992                     $t = $this->rfread($f, $n);
04993                     if ($ct == 0) {
04994                         $trns = array(ord(substr($t, 1, 1)));
04995                     } elseif ($ct == 2) {
04996                         $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
04997                     } else {
04998                         $pos = strpos($t, chr(0));
04999                         if ($pos !== false) {
05000                             $trns = array($pos);
05001                         }
05002                     }
05003                     fread($f, 4);
05004                 } elseif ($type == 'IDAT') {
05005                     //Read image data block
05006                     $data .= $this->rfread($f, $n);
05007                     fread($f, 4);
05008                 } elseif ($type == 'IEND') {
05009                     break;
05010                 } else {
05011                     $this->rfread($f, $n + 4);
05012                 }
05013             } while ($n);
05014             if (($colspace == 'Indexed') AND (empty($pal))) {
05015                 //$this->Error('Missing palette in '.$file);
05016                 fclose($f);
05017                 return false;
05018             }
05019             fclose($f);
05020             return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
05021         }
05022 
05033         protected function rfread($handle, $length) {
05034             $data = fread($handle, $length);
05035             if ($data === false) {
05036                 return false;
05037             }
05038             $rest = $length - strlen($data);
05039             if ($rest > 0) {
05040                 $data .= $this->rfread($handle, $rest);
05041             }
05042             return $data;
05043         }
05044 
05063         protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
05064             // get image size
05065             list($wpx, $hpx) = getimagesize($file);
05066             // generate images
05067             $img = imagecreatefrompng($file);
05068             $imgalpha = imagecreate($wpx, $hpx);
05069             // generate gray scale pallete
05070             for ($c = 0; $c < 256; ++$c) {
05071                 ImageColorAllocate($imgalpha, $c, $c, $c);
05072             }
05073             // extract alpha channel
05074             for ($xpx = 0; $xpx < $wpx; ++$xpx) {
05075                 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
05076                     $colorindex = imagecolorat($img, $xpx, $ypx);
05077                     $col = imagecolorsforindex($img, $colorindex);
05078                     imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127));
05079                 }
05080             }
05081             // create temp alpha file
05082             $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
05083             imagepng($imgalpha, $tempfile_alpha);
05084             imagedestroy($imgalpha);
05085             // extract image without alpha channel
05086             $imgplain = imagecreatetruecolor($wpx, $hpx);
05087             imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
05088             // create temp image file
05089             $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
05090             imagepng($imgplain, $tempfile_plain);
05091             imagedestroy($imgplain);
05092             // embed mask image
05093             $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
05094             // embed image, masked with previously embedded mask
05095             $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
05096             // remove temp files
05097             unlink($tempfile_alpha);
05098             unlink($tempfile_plain);
05099         }
05100 
05107         protected function getGDgamma($v) {
05108             return (pow(($v / 255), 2.2) * 255);
05109         } 
05110         
05120         public function Ln($h='', $cell=false) {
05121             //Line feed; default value is last cell height
05122             if ($cell) {
05123                 $cellmargin = $this->cMargin;
05124             } else {
05125                 $cellmargin = 0;
05126             }
05127             if ($this->rtl) {
05128                 $this->x = $this->w - $this->rMargin - $cellmargin;
05129             } else {
05130                 $this->x = $this->lMargin + $cellmargin;
05131             }
05132             if (is_string($h)) {
05133                 $this->y += $this->lasth;
05134             } else {
05135                 $this->y += $h;
05136             }
05137             $this->newline = true;
05138         }
05139 
05148         public function GetX() {
05149             //Get x position
05150             if ($this->rtl) {
05151                 return ($this->w - $this->x);
05152             } else {
05153                 return $this->x;
05154             }
05155         }
05156         
05164         public function GetAbsX() {
05165             return $this->x;
05166         }
05167         
05175         public function GetY() {
05176             //Get y position
05177             return $this->y;
05178         }
05179         
05188         public function SetX($x) {
05189             //Set x position
05190             if ($this->rtl) {
05191                 if ($x >= 0) {
05192                     $this->x = $this->w - $x;
05193                 } else {
05194                     $this->x = abs($x);
05195                 }
05196             } else {
05197                 if ($x >= 0) {
05198                     $this->x = $x;
05199                 } else {
05200                     $this->x = $this->w + $x;
05201                 }
05202             }
05203             if ($this->x < 0) {
05204                 $this->x = 0;
05205             }
05206             if ($this->x > $this->w) {
05207                 $this->x = $this->w;
05208             }
05209         }
05210         
05220         public function SetY($y, $resetx=true) {
05221             if ($resetx) {
05222                 //reset x
05223                 if ($this->rtl) {
05224                     $this->x = $this->w - $this->rMargin;
05225                 } else {
05226                     $this->x = $this->lMargin;
05227                 }
05228             }
05229             if ($y >= 0) {
05230                 $this->y = $y;
05231             } else {
05232                 $this->y = $this->h + $y;
05233             }
05234             if ($this->y < 0) {
05235                 $this->y = 0;
05236             }
05237             if ($this->y > $this->h) {
05238                 $this->y = $this->h;
05239             }
05240         }
05241         
05251         public function SetXY($x, $y) {
05252             //Set x and y positions
05253             $this->SetY($y);
05254             $this->SetX($x);
05255         }
05256 
05267         public function Output($name='doc.pdf', $dest='I') {
05268             //Output PDF to some destination
05269             //Finish document if necessary
05270             if ($this->state < 3) {
05271                 $this->Close();
05272             }
05273             //Normalize parameters
05274             if (is_bool($dest)) {
05275                 $dest = $dest ? 'D' : 'F';
05276             }
05277             $dest = strtoupper($dest);
05278             if ($dest != 'F') {
05279                 $name = preg_replace('/[\s]+/', '_', $name);
05280                 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
05281             }
05282             if ($this->sign) {
05283                 // *** apply digital signature to the document ***
05284                 // get the document content
05285                 $pdfdoc = $this->getBuffer();
05286                 // remove last newline
05287                 $pdfdoc = substr($pdfdoc, 0, -1);
05288                 // Remove the original buffer
05289                 if (isset($this->diskcache) AND $this->diskcache) {
05290                     // remove buffer file from cache
05291                     unlink($this->buffer);
05292                 }
05293                 unset($this->buffer);
05294                 // remove filler space
05295                 $byterange_string_len = strlen($this->byterange_string);
05296                 // define the ByteRange
05297                 $byte_range = array();
05298                 $byte_range[0] = 0;
05299                 $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10;
05300                 $byte_range[2] = $byte_range[1] + $this->signature_max_lenght + 2;
05301                 $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
05302                 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
05303                 // replace the ByteRange
05304                 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
05305                 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
05306                 $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc);
05307                 // write the document to a temporary folder
05308                 $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
05309                 $f = fopen($tempdoc, 'wb');
05310                 if (!$f) {
05311                     $this->Error('Unable to create temporary file: '.$tempdoc);
05312                 }
05313                 $pdfdoc_lenght = strlen($pdfdoc);
05314                 fwrite($f, $pdfdoc, $pdfdoc_lenght);
05315                 fclose($f);
05316                 // get digital signature via openssl library
05317                 $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
05318                 if (empty($this->signature_data['extracerts'])) {
05319                     openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
05320                 } else {
05321                     openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']);
05322                 }   
05323                 unlink($tempdoc);
05324                 // read signature
05325                 $signature = file_get_contents($tempsign, false, null, $pdfdoc_lenght);
05326                 unlink($tempsign);
05327                 // extract signature
05328                 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
05329                 $tmparr = explode("\n\n", $signature);
05330                 $signature = $tmparr[1];
05331                 unset($tmparr);
05332                 // decode signature
05333                 $signature = base64_decode(trim($signature));
05334                 // convert signature to hex
05335                 $signature = current(unpack('H*', $signature));
05336                 $signature = str_pad($signature, $this->signature_max_lenght, '0');
05337                 // Add signature to the document
05338                 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, ($byte_range[1]));
05339                 $this->diskcache = false;
05340                 $this->buffer = &$pdfdoc;
05341                 $this->bufferlen = strlen($pdfdoc);
05342             }
05343             switch($dest) {
05344                 case 'I': {
05345                     // Send PDF to the standard output
05346                     if (ob_get_contents()) {
05347                         $this->Error('Some data has already been output, can\'t send PDF file');
05348                     }
05349                     if (php_sapi_name() != 'cli') {
05350                         //We send to a browser
05351                         header('Content-Type: application/pdf');
05352                         if (headers_sent()) {
05353                             $this->Error('Some data has already been output to browser, can\'t send PDF file');
05354                         }
05355                         header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
05356                         header('Pragma: public');
05357                         header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
05358                         header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');  
05359                         header('Content-Length: '.$this->bufferlen);
05360                         header('Content-Disposition: inline; filename="'.basename($name).'";');
05361                     }
05362                     echo $this->getBuffer();
05363                     break;
05364                 }
05365                 case 'D': {
05366                     // Download PDF as file
05367                     if (ob_get_contents()) {
05368                         $this->Error('Some data has already been output, can\'t send PDF file');
05369                     }
05370                     header('Content-Description: File Transfer');
05371                     if (headers_sent()) {
05372                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
05373                     }
05374                     header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
05375                     header('Pragma: public');
05376                     header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
05377                     header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
05378                     // force download dialog
05379                     header('Content-Type: application/force-download');
05380                     header('Content-Type: application/octet-stream', false);
05381                     header('Content-Type: application/download', false);
05382                     header('Content-Type: application/pdf', false);
05383                     // use the Content-Disposition header to supply a recommended filename
05384                     header('Content-Disposition: attachment; filename="'.basename($name).'";');
05385                     header('Content-Transfer-Encoding: binary');
05386                     header('Content-Length: '.$this->bufferlen);
05387                     echo $this->getBuffer();
05388                     break;
05389                 }
05390                 case 'F': {
05391                     // Save PDF to a local file
05392                     if ($this->diskcache) {
05393                         copy($this->buffer, $name);
05394                     } else {
05395                         $f = fopen($name, 'wb');
05396                         if (!$f) {
05397                             $this->Error('Unable to create output file: '.$name);
05398                         }
05399                         fwrite($f, $this->getBuffer(), $this->bufferlen);
05400                         fclose($f);
05401                     }
05402                     break;
05403                 }
05404                 case 'S': {
05405                     // Returns PDF as a string
05406                     return $this->getBuffer();
05407                 }
05408                 default: {
05409                     $this->Error('Incorrect output destination: '.$dest);
05410                 }
05411             }
05412             return '';
05413         }
05414 
05422         public function _destroy($destroyall=false, $preserve_objcopy=false) {
05423             if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
05424                 // remove buffer file from cache
05425                 unlink($this->buffer);
05426             }
05427             foreach (array_keys(get_object_vars($this)) as $val) {
05428                 if ($destroyall OR (
05429                     ($val != 'internal_encoding') 
05430                     AND ($val != 'state') 
05431                     AND ($val != 'bufferlen') 
05432                     AND ($val != 'buffer') 
05433                     AND ($val != 'diskcache')
05434                     AND ($val != 'sign')
05435                     AND ($val != 'signature_data')
05436                     AND ($val != 'signature_max_lenght')
05437                     AND ($val != 'byterange_string')
05438                     )) {
05439                     if (!$preserve_objcopy OR ($val != 'objcopy')) {
05440                         unset($this->$val);
05441                     }
05442                 }
05443             }
05444         }
05445         
05450         protected function _dochecks() {
05451             //Check for locale-related bug
05452             if (1.1 == 1) {
05453                 $this->Error('Don\'t alter the locale before including class file');
05454             }
05455             //Check for decimal separator
05456             if (sprintf('%.1F', 1.0) != '1.0') {
05457                 setlocale(LC_NUMERIC, 'C');
05458             }
05459         }
05460 
05466         protected function _getfontpath() {
05467             if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
05468                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
05469             }
05470             return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
05471         }
05472         
05477         protected function _putpages() {
05478             $nb = $this->numpages;
05479             if (!empty($this->AliasNbPages)) {
05480                 $nbs = $this->formatPageNumber($nb);
05481                 $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
05482                 $alias_a = $this->_escape($this->AliasNbPages);
05483                 $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
05484                 if ($this->isunicode) {
05485                     $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
05486                     $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
05487                     $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
05488                     $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
05489                 }
05490             }
05491             if (!empty($this->AliasNumPage)) {
05492                 $alias_pa = $this->_escape($this->AliasNumPage);
05493                 $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
05494                 if ($this->isunicode) {
05495                     $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
05496                     $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
05497                     $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
05498                     $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
05499                 }
05500             }
05501             $pagegroupnum = 0;
05502             $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
05503             for ($n=1; $n <= $nb; ++$n) {
05504                 $temppage = $this->getPageBuffer($n);
05505                 if (!empty($this->pagegroups)) {
05506                     if(isset($this->newpagegroup[$n])) {
05507                         $pagegroupnum = 0;
05508                     }
05509                     ++$pagegroupnum;
05510                     foreach ($this->pagegroups as $k => $v) {
05511                         // replace total pages group numbers
05512                         $vs = $this->formatPageNumber($v);
05513                         $vu = $this->UTF8ToUTF16BE($vs, false);
05514                         $alias_ga = $this->_escape($k);
05515                         $alias_gau = $this->_escape('{'.$k.'}');
05516                         if ($this->isunicode) {
05517                             $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
05518                             $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
05519                             $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
05520                             $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
05521                         }
05522                         $temppage = str_replace($alias_gau, $vu, $temppage);
05523                         if ($this->isunicode) {
05524                             $temppage = str_replace($alias_gbu, $vu, $temppage);
05525                             $temppage = str_replace($alias_gcu, $vu, $temppage);
05526                             $temppage = str_replace($alias_gb, $vs, $temppage);
05527                             $temppage = str_replace($alias_gc, $vs, $temppage);
05528                         }
05529                         $temppage = str_replace($alias_ga, $vs, $temppage);
05530                         // replace page group numbers
05531                         $pvs = $this->formatPageNumber($pagegroupnum);
05532                         $pvu = $this->UTF8ToUTF16BE($pvs, false);
05533                         $pk = str_replace('{nb', '{pnb', $k);
05534                         $alias_pga = $this->_escape($pk);
05535                         $alias_pgau = $this->_escape('{'.$pk.'}');
05536                         if ($this->isunicode) {
05537                             $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
05538                             $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
05539                             $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
05540                             $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
05541                         }
05542                         $temppage = str_replace($alias_pgau, $pvu, $temppage);
05543                         if ($this->isunicode) {
05544                             $temppage = str_replace($alias_pgbu, $pvu, $temppage);
05545                             $temppage = str_replace($alias_pgcu, $pvu, $temppage);
05546                             $temppage = str_replace($alias_pgb, $pvs, $temppage);
05547                             $temppage = str_replace($alias_pgc, $pvs, $temppage);
05548                         }
05549                         $temppage = str_replace($alias_pga, $pvs, $temppage);
05550                     }
05551                 }
05552                 if (!empty($this->AliasNbPages)) {
05553                     // replace total pages number
05554                     $temppage = str_replace($alias_au, $nbu, $temppage);
05555                     if ($this->isunicode) {
05556                         $temppage = str_replace($alias_bu, $nbu, $temppage);
05557                         $temppage = str_replace($alias_cu, $nbu, $temppage);
05558                         $temppage = str_replace($alias_b, $nbs, $temppage);
05559                         $temppage = str_replace($alias_c, $nbs, $temppage);
05560                     }
05561                     $temppage = str_replace($alias_a, $nbs, $temppage);
05562                 }
05563                 if (!empty($this->AliasNumPage)) {
05564                     // replace page number
05565                     $pnbs = $this->formatPageNumber($n);
05566                     $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
05567                     $temppage = str_replace($alias_pau, $pnbu, $temppage);
05568                     if ($this->isunicode) {
05569                         $temppage = str_replace($alias_pbu, $pnbu, $temppage);
05570                         $temppage = str_replace($alias_pcu, $pnbu, $temppage);
05571                         $temppage = str_replace($alias_pb, $pnbs, $temppage);
05572                         $temppage = str_replace($alias_pc, $pnbs, $temppage);
05573                     }
05574                     $temppage = str_replace($alias_pa, $pnbs, $temppage);
05575                 }
05576                 $temppage = str_replace($this->epsmarker, '', $temppage);
05577                 //Page
05578                 $this->page_obj_id[$n] = $this->_newobj();
05579                 $this->_out('<</Type /Page');
05580                 $this->_out('/Parent 1 0 R');
05581                 $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
05582                 $this->_out('/Resources 2 0 R');
05583                 $this->_putannotsrefs($n);
05584                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
05585                 $this->_out('endobj');
05586                 //Page content
05587                 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
05588                 $this->_newobj();
05589                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
05590                 $this->_putstream($p);
05591                 $this->_out('endobj');
05592                 if ($this->diskcache) {
05593                     // remove temporary files
05594                     unlink($this->pages[$n]);
05595                 }
05596             }
05597             //Pages root
05598             $this->offsets[1] = $this->bufferlen;
05599             $this->_out('1 0 obj');
05600             $this->_out('<</Type /Pages');
05601             $this->_out('/Kids [');
05602             foreach($this->page_obj_id as $page_obj) {
05603                 $this->_out($page_obj.' 0 R');
05604             }
05605             $this->_out(']');
05606             $this->_out('/Count '.$nb);
05607             $this->_out('>>');
05608             $this->_out('endobj');
05609         }
05610 
05618         protected function _putannotsrefs($n) {
05619             if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
05620                 return;
05621             }
05622             $this->_out('/Annots [');
05623             if (isset($this->PageAnnots[$n])) {
05624                 $num_annots = count($this->PageAnnots[$n]);
05625                 for ($i = 0; $i < $num_annots; ++$i) {
05626                     ++$this->curr_annot_obj_id;
05627                     if (!in_array($this->curr_annot_obj_id, $this->radio_groups)) {
05628                         $this->_out($this->curr_annot_obj_id.' 0 R');
05629                     } else {
05630                         ++$num_annots;
05631                     }
05632                 }
05633             }
05634             if (($n==1) AND $this->sign AND isset($this->signature_data['cert_type'])) {
05635                 // set reference for signature object
05636                 $this->_out($this->sig_annot_ref);
05637             }
05638             $this->_out(']');
05639         }
05640 
05649         protected function _putannotsobjs() {
05650             // reset object counter
05651             $this->annot_obj_id = $this->annots_start_obj_id;
05652             for ($n=1; $n <= $this->numpages; ++$n) {
05653                 if (isset($this->PageAnnots[$n])) {
05654                     // set page annotations
05655                     foreach ($this->PageAnnots[$n] as $key => $pl) {
05656                         // create annotation object for grouping radiobuttons
05657                         if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
05658                             $annots = '<<';
05659                             $annots .= ' /Type /Annot';
05660                             $annots .= ' /Subtype /Widget';
05661                             $annots .= ' /T '.$this->_datastring($pl['txt']);
05662                             $annots .= ' /FT /Btn';
05663                             $annots .= ' /Ff 49152';
05664                             $annots .= ' /Kids [';
05665                             foreach ($this->radiobutton_groups[$n][$pl['txt']] as $data) {
05666                                 $annots .= ' '.$data['kid'].' 0 R';
05667                                 if ($data['def'] !== 'Off') {
05668                                     $defval = $data['def'];
05669                                 }
05670                             }
05671                             $annots .= ' ]';
05672                             if (isset($defval)) {
05673                                 $annots .= ' /V /'.$defval;
05674                             }
05675                             $annots .= ' >>';
05676                             ++$this->annot_obj_id;
05677                             $this->offsets[$this->annot_obj_id] = $this->bufferlen;
05678                             $this->_out($this->annot_obj_id.' 0 obj');
05679                             $this->_out($annots);
05680                             $this->_out('endobj');
05681                             $this->form_obj_id[] = $this->annot_obj_id;
05682                             // store object id to be used on Parent entry of Kids
05683                             $this->radiobutton_groups[$n][$pl['txt']] = $this->annot_obj_id;
05684                         }
05685                         $formfield = false;
05686                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
05687                         $a = $pl['x'] * $this->k;
05688                         $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h'])  * $this->k);
05689                         $c = $pl['w'] * $this->k;
05690                         $d = $pl['h'] * $this->k;
05691                         $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d);
05692                         // create new annotation object
05693                         $annots = '<</Type /Annot';
05694                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
05695                         $annots .= ' /Rect ['.$rect.']';
05696                         $ft = array('Btn', 'Tx', 'Ch', 'Sig');
05697                         if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
05698                             $annots .= ' /FT /'.$pl['opt']['ft'];
05699                             $formfield = true;
05700                         }
05701                         $annots .= ' /Contents '.$this->_textstring($pl['txt']);
05702                         $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
05703                         $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key));
05704                         $annots .= ' /M '.$this->_datestring();
05705                         if (isset($pl['opt']['f'])) {
05706                             $val = 0;
05707                             if (is_array($pl['opt']['f'])) {
05708                                 foreach ($pl['opt']['f'] as $f) {
05709                                     switch (strtolower($f)) {
05710                                         case 'invisible': {
05711                                             $val += 1 << 0;
05712                                             break;
05713                                         }
05714                                         case 'hidden': {
05715                                             $val += 1 << 1;
05716                                             break;
05717                                         }
05718                                         case 'print': {
05719                                             $val += 1 << 2;
05720                                             break;
05721                                         }
05722                                         case 'nozoom': {
05723                                             $val += 1 << 3;
05724                                             break;
05725                                         }
05726                                         case 'norotate': {
05727                                             $val += 1 << 4;
05728                                             break;
05729                                         }
05730                                         case 'noview': {
05731                                             $val += 1 << 5;
05732                                             break;
05733                                         }
05734                                         case 'readonly': {
05735                                             $val += 1 << 6;
05736                                             break;
05737                                         }
05738                                         case 'locked': {
05739                                             $val += 1 << 8;
05740                                             break;
05741                                         }
05742                                         case 'togglenoview': {
05743                                             $val += 1 << 9;
05744                                             break;
05745                                         }
05746                                         case 'lockedcontents': {
05747                                             $val += 1 << 10;
05748                                             break;
05749                                         }
05750                                         default: {
05751                                             break;
05752                                         }
05753                                     }
05754                                 }
05755                             } else {
05756                                 $val = intval($pl['opt']['f']);
05757                             }
05758                             $annots .= ' /F '.intval($val);
05759                         }
05760                         if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
05761                             $annots .= ' /AS /'.$pl['opt']['as'];
05762                         }
05763                         if (isset($pl['opt']['ap'])) {
05764                             // appearance stream
05765                             $annots .= ' /AP <<';
05766                             if (is_array($pl['opt']['ap'])) {
05767                                 foreach ($pl['opt']['ap'] as $apmode => $apdef) {
05768                                     // $apmode can be: n = normal; r = rollover; d = down;
05769                                     $annots .= ' /'.strtoupper($apmode);
05770                                     if (is_array($apdef)) {
05771                                         $annots .= ' <<';
05772                                         foreach ($apdef as $apstate => $stream) {
05773                                             // reference to XObject that define the appearance for this mode-state
05774                                             $apsobjid = $this->_putAPXObject($c, $d, $stream);
05775                                             $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
05776                                         }
05777                                         $annots .= ' >>';
05778                                     } else {
05779                                         // reference to XObject that define the appearance for this mode
05780                                         $apsobjid = $this->_putAPXObject($c, $d, $apdef);
05781                                         $annots .= ' '.$apsobjid.' 0 R';
05782                                     }
05783                                 }
05784                             } else {
05785                                 $annots .= $pl['opt']['ap'];
05786                             }
05787                             $annots .= ' >>';
05788                         }
05789                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
05790                             $annots .= ' /BS <<';
05791                             $annots .= ' /Type /Border';
05792                             if (isset($pl['opt']['bs']['w'])) {
05793                                 $annots .= ' /W '.intval($pl['opt']['bs']['w']);
05794                             }
05795                             $bstyles = array('S', 'D', 'B', 'I', 'U');
05796                             if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
05797                                 $annots .= ' /S /'.$pl['opt']['bs']['s'];
05798                             }
05799                             if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
05800                                 $annots .= ' /D [';
05801                                 foreach ($pl['opt']['bs']['d'] as $cord) {
05802                                     $annots .= ' '.intval($cord);
05803                                 }
05804                                 $annots .= ']';
05805                             }
05806                             $annots .= ' >>';
05807                         } else {
05808                             $annots .= ' /Border [';
05809                             if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
05810                                 $annots .= intval($pl['opt']['border'][0]).' ';
05811                                 $annots .= intval($pl['opt']['border'][1]).' ';
05812                                 $annots .= intval($pl['opt']['border'][2]);
05813                                 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
05814                                     $annots .= ' [';
05815                                     foreach ($pl['opt']['border'][3] as $dash) {
05816                                         $annots .= intval($dash).' ';
05817                                     }
05818                                     $annots .= ']';
05819                                 }
05820                             } else {
05821                                 $annots .= '0 0 0';
05822                             }
05823                             $annots .= ']';
05824                         }
05825                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
05826                             $annots .= ' /BE <<';
05827                             $bstyles = array('S', 'C');
05828                             if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
05829                                 $annots .= ' /S /'.$pl['opt']['bs']['s'];
05830                             } else {
05831                                 $annots .= ' /S /S';
05832                             }
05833                             if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
05834                                 $annots .= ' /I '.sprintf(" %.4F", $pl['opt']['be']['i']);
05835                             }
05836                             $annots .= '>>';
05837                         }
05838                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
05839                             $annots .= ' /C [';
05840                             foreach ($pl['opt']['c'] as $col) {
05841                                 $col = intval($col);
05842                                 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
05843                                 $annots .= sprintf(" %.4F", $color);
05844                             }
05845                             $annots .= ']';
05846                         }
05847                         //$annots .= ' /StructParent ';
05848                         //$annots .= ' /OC ';
05849                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
05850                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
05851                             // this is a markup type
05852                             if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
05853                                 $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
05854                             }
05855                             //$annots .= ' /Popup ';
05856                             if (isset($pl['opt']['ca'])) {
05857                                 $annots .= ' /CA '.sprintf("%.4F", floatval($pl['opt']['ca']));
05858                             }
05859                             if (isset($pl['opt']['rc'])) {
05860                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
05861                             }
05862                             $annots .= ' /CreationDate '.$this->_datestring();
05863                             //$annots .= ' /IRT ';
05864                             if (isset($pl['opt']['subj'])) {
05865                                 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
05866                             }
05867                             //$annots .= ' /RT ';
05868                             //$annots .= ' /IT ';
05869                             //$annots .= ' /ExData ';
05870                         }
05871                         $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
05872                         switch (strtolower($pl['opt']['subtype'])) {
05873                             case 'text': {
05874                                 if (isset($pl['opt']['open'])) {
05875                                     $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
05876                                 }
05877                                 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
05878                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
05879                                     $annots .= ' /Name /'.$pl['opt']['name'];
05880                                 } else {
05881                                     $annots .= ' /Name /Note';
05882                                 }
05883                                 $statemodels = array('Marked', 'Review');
05884                                 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
05885                                     $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
05886                                 } else {
05887                                     $pl['opt']['statemodel'] = 'Marked';
05888                                     $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
05889                                 }
05890                                 if ($pl['opt']['statemodel'] == 'Marked') {
05891                                     $states = array('Accepted', 'Unmarked');
05892                                 } else {
05893                                     $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
05894                                 }
05895                                 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
05896                                     $annots .= ' /State /'.$pl['opt']['state'];
05897                                 } else {
05898                                     if ($pl['opt']['statemodel'] == 'Marked') {
05899                                         $annots .= ' /State /Unmarked';
05900                                     } else {
05901                                         $annots .= ' /State /None';
05902                                     }
05903                                 }
05904                                 break;
05905                             }
05906                             case 'link': {
05907                                 if(is_string($pl['txt'])) {
05908                                     // external URI link
05909                                     $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt'])).'>>';
05910                                 } else {
05911                                     // internal link
05912                                     $l = $this->links[$pl['txt']];
05913                                     $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
05914                                 }
05915                                 $hmodes = array('N', 'I', 'O', 'P');
05916                                 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
05917                                     $annots .= ' /H /'.$pl['opt']['h'];
05918                                 } else {
05919                                     $annots .= ' /H /I';
05920                                 }
05921                                 //$annots .= ' /PA ';
05922                                 //$annots .= ' /Quadpoints ';
05923                                 break;
05924                             }
05925                             case 'freetext': {
05926                                 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
05927                                     $annots .= ' /DA ('.$pl['opt']['da'].')';
05928                                 }
05929                                 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
05930                                     $annots .= ' /Q '.intval($pl['opt']['q']);
05931                                 }
05932                                 if (isset($pl['opt']['rc'])) {
05933                                     $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
05934                                 }
05935                                 if (isset($pl['opt']['ds'])) {
05936                                     $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
05937                                 }
05938                                 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
05939                                     $annots .= ' /CL [';
05940                                     foreach ($pl['opt']['cl'] as $cl) {
05941                                         $annots .= sprintf("%.4F ", $cl * $this->k);
05942                                     }
05943                                     $annots .= ']';
05944                                 }
05945                                 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
05946                                 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
05947                                     $annots .= ' /IT '.$pl['opt']['it'];
05948                                 }
05949                                 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
05950                                     $l = $pl['opt']['rd'][0] * $this->k;
05951                                     $r = $pl['opt']['rd'][1] * $this->k;
05952                                     $t = $pl['opt']['rd'][2] * $this->k;
05953                                     $b = $pl['opt']['rd'][3] * $this->k;
05954                                     $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
05955                                 }
05956                                 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
05957                                     $annots .= ' /LE /'.$pl['opt']['le'];
05958                                 }
05959                                 break;
05960                             }
05961                             case 'line': {
05962                                 break;
05963                             }
05964                             case 'square': {
05965                                 break;
05966                             }
05967                             case 'circle': {
05968                                 break;
05969                             }
05970                             case 'polygon': {
05971                                 break;
05972                             }
05973                             case 'polyline': {
05974                                 break;
05975                             }
05976                             case 'highlight': {
05977                                 break;
05978                             }
05979                             case 'underline': {
05980                                 break;
05981                             }
05982                             case 'squiggly': {
05983                                 break;
05984                             }
05985                             case 'strikeout': {
05986                                 break;
05987                             }
05988                             case 'stamp': {
05989                                 break;
05990                             }
05991                             case 'caret': {
05992                                 break;
05993                             }
05994                             case 'ink': {
05995                                 break;
05996                             }
05997                             case 'popup': {
05998                                 break;
05999                             }
06000                             case 'fileattachment': {
06001                                 if (!isset($pl['opt']['fs'])) {
06002                                     break;
06003                                 }
06004                                 $filename = basename($pl['opt']['fs']);
06005                                 if (isset($this->embeddedfiles[$filename]['n'])) {
06006                                     $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
06007                                     $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
06008                                     if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
06009                                         $annots .= ' /Name /'.$pl['opt']['name'];
06010                                     } else {
06011                                         $annots .= ' /Name /PushPin';
06012                                     }
06013                                 }
06014                                 break;
06015                             }
06016                             case 'sound': {
06017                                 if (!isset($pl['opt']['sound'])) {
06018                                     break;
06019                                 }
06020                                 $filename = basename($pl['opt']['sound']);
06021                                 if (isset($this->embeddedfiles[$filename]['n'])) {
06022                                     $annots .= ' /Sound <</Type /Sound';
06023                                     // ... TO BE COMPLETED ...
06024                                     // /R /C /B /E /CO /CP
06025                                     // $annots .= ' /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
06026                                     $iconsapp = array('Speaker', 'Mic');
06027                                     if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
06028                                         $annots .= ' /Name /'.$pl['opt']['name'];
06029                                     } else {
06030                                         $annots .= ' /Name /Speaker';
06031                                     }
06032                                 }
06033                                 break;
06034                             }
06035                             case 'movie': {
06036                                 break;
06037                             }
06038                             case 'widget': {
06039                                 $hmode = array('N', 'I', 'O', 'P', 'T');
06040                                 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
06041                                     $annots .= ' /H /'.$pl['opt']['h'];
06042                                 }
06043                                 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
06044                                     $annots .= ' /MK <<';
06045                                     if (isset($pl['opt']['mk']['r'])) {
06046                                         $annots .= ' /R '.$pl['opt']['mk']['r'];
06047                                     }
06048                                     if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
06049                                         $annots .= ' /BC [';
06050                                         foreach($pl['opt']['mk']['bc'] AS $col) {
06051                                             $col = intval($col);
06052                                             $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
06053                                             $annots .= ' '.$color;
06054                                         }
06055                                         $annots .= ']';
06056                                     }
06057                                     if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
06058                                         $annots .= ' /BG [';
06059                                         foreach($pl['opt']['mk']['bg'] AS $col) {
06060                                             $col = intval($col);
06061                                             $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
06062                                             $annots .= ' '.$color;
06063                                         }
06064                                         $annots .= ']';
06065                                     }
06066                                     if (isset($pl['opt']['mk']['ca'])) {
06067                                         $annots .= ' /CA '.$pl['opt']['mk']['ca'].'';
06068                                     }
06069                                     if (isset($pl['opt']['mk']['rc'])) {
06070                                         $annots .= ' /RC '.$pl['opt']['mk']['ca'].'';
06071                                     }
06072                                     if (isset($pl['opt']['mk']['ac'])) {
06073                                         $annots .= ' /AC '.$pl['opt']['mk']['ca'].'';
06074                                     }                                                                   
06075                                     if (isset($pl['opt']['mk']['i'])) {
06076                                         $info = $this->getImageBuffer($pl['opt']['mk']['i']);
06077                                         if ($info !== false) {
06078                                             $annots .= ' /I '.$info['n'].' 0 R';
06079                                         }
06080                                     }
06081                                     if (isset($pl['opt']['mk']['ri'])) {
06082                                         $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
06083                                         if ($info !== false) {
06084                                             $annots .= ' /RI '.$info['n'].' 0 R';
06085                                         }
06086                                     }
06087                                     if (isset($pl['opt']['mk']['ix'])) {
06088                                         $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
06089                                         if ($info !== false) {
06090                                             $annots .= ' /IX '.$info['n'].' 0 R';
06091                                         }
06092                                     }                                   
06093                                     if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
06094                                         $annots .= ' /IF <<';
06095                                         $if_sw = array('A', 'B', 'S', 'N');
06096                                         if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
06097                                             $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
06098                                         }
06099                                         $if_s = array('A', 'P');
06100                                         if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
06101                                             $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
06102                                         }
06103                                         if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
06104                                             $annots .= ' /A ['.$pl['opt']['mk']['if']['a'][0].' '.$pl['opt']['mk']['if']['a'][1].']';
06105                                         }
06106                                         if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
06107                                             $annots .= ' /FB true';
06108                                         }
06109                                         $annots .= '>>';
06110                                     }
06111                                     if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
06112                                         $annots .= ' /TP '.$pl['opt']['mk']['tp'];
06113                                     } else {
06114                                         $annots .= ' /TP 0';
06115                                     }
06116                                     $annots .= '>>';
06117                                 } // end MK
06118                                 // --- Entries for field dictionaries ---
06119                                 if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
06120                                     // set parent
06121                                     $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
06122                                 }
06123                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
06124                                     $annots .= ' /T '.$this->_datastring($pl['opt']['t']);
06125                                 }
06126                                 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
06127                                     $annots .= ' /TU '.$this->_datastring($pl['opt']['tu']);
06128                                 }
06129                                 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
06130                                     $annots .= ' /TM '.$this->_datastring($pl['opt']['tm']);
06131                                 }
06132                                 if (isset($pl['opt']['ff'])) {
06133                                     if (is_array($pl['opt']['ff'])) {
06134                                         // array of bit settings
06135                                         $flag = 0;
06136                                         foreach($pl['opt']['ff'] as $val) {
06137                                             $flag += 1 << ($val - 1);
06138                                         }
06139                                     } else {
06140                                         $flag = intval($pl['opt']['ff']);
06141                                     }
06142                                     $annots .= ' /Ff '.$flag;
06143                                 }
06144                                 if (isset($pl['opt']['maxlen'])) {
06145                                     $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
06146                                 }
06147                                 if (isset($pl['opt']['v'])) {
06148                                     $annots .= ' /V';
06149                                     if (is_array($pl['opt']['v'])) {
06150                                         foreach ($pl['opt']['v'] AS $optval) {
06151                                             $annots .= ' '.$optval;
06152                                         }
06153                                     } else {
06154                                         $annots .= ' '.$this->_textstring($pl['opt']['v']);
06155                                     }
06156                                 }
06157                                 if (isset($pl['opt']['dv']) AND is_string($pl['opt']['dv'])) {
06158                                     $annots .= ' /DV';
06159                                     if (is_array($pl['opt']['dv'])) {
06160                                         foreach ($pl['opt']['dv'] AS $optval) {
06161                                             $annots .= ' '.$optval;
06162                                         }
06163                                     } else {
06164                                         $annots .= ' '.$this->_textstring($pl['opt']['dv']);
06165                                     }
06166                                 }
06167                                 if (isset($pl['opt']['rv']) AND is_string($pl['opt']['rv'])) {
06168                                     $annots .= ' /RV';
06169                                     if (is_array($pl['opt']['rv'])) {
06170                                         foreach ($pl['opt']['rv'] AS $optval) {
06171                                             $annots .= ' '.$optval;
06172                                         }
06173                                     } else {
06174                                         $annots .= ' '.$this->_textstring($pl['opt']['rv']);
06175                                     }
06176                                 }
06177                                 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
06178                                     $annots .= ' /A << '.$pl['opt']['a'].' >>';
06179                                 }
06180                                 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
06181                                     $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
06182                                 }
06183                                 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
06184                                     $annots .= ' /DA ('.$pl['opt']['da'].')';
06185                                 }
06186                                 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
06187                                     $annots .= ' /Q '.intval($pl['opt']['q']);
06188                                 }
06189                                 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
06190                                     $annots .= ' /Opt [';
06191                                     foreach($pl['opt']['opt'] AS $copt) {
06192                                         if (is_array($copt)) {
06193                                             $annots .= ' ['.$this->_textstring($copt[0]).' '.$this->_textstring($copt[1]).']';
06194                                         } else {
06195                                             $annots .= ' '.$this->_textstring($copt);
06196                                         }
06197                                     }
06198                                     $annots .= ']';
06199                                 }
06200                                 if (isset($pl['opt']['ti'])) {
06201                                     $annots .= ' /TI '.intval($pl['opt']['ti']);
06202                                 }
06203                                 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
06204                                     $annots .= ' /I [';
06205                                     foreach($pl['opt']['i'] AS $copt) {
06206                                         $annots .= intval($copt).' ';
06207                                     }
06208                                     $annots .= ']';
06209                                 }
06210                                 break;
06211                             }
06212                             case 'screen': {
06213                                 break;
06214                             }
06215                             case 'printermark': {
06216                                 break;
06217                             }
06218                             case 'trapnet': {
06219                                 break;
06220                             }
06221                             case 'watermark': {
06222                                 break;
06223                             }
06224                             case '3d': {
06225                                 break;
06226                             }
06227                             default: {
06228                                 break;
06229                             }
06230                         }
06231                         $annots .= '>>';
06232                         // create new annotation object
06233                         ++$this->annot_obj_id;
06234                         $this->offsets[$this->annot_obj_id] = $this->bufferlen;
06235                         $this->_out($this->annot_obj_id.' 0 obj');
06236                         $this->_out($annots);
06237                         $this->_out('endobj');
06238                         if ($formfield AND ! isset($this->radiobutton_groups[$n][$pl['txt']])) {
06239                             // store reference of form object
06240                             $this->form_obj_id[] = $this->annot_obj_id;
06241                         }
06242                     }
06243                 }
06244             } // end for each page
06245         }
06246 
06256         protected function _putAPXObject($w=0, $h=0, $stream='') {
06257             $stream = trim($stream);
06258             ++$this->apxo_obj_id;
06259             $this->offsets[$this->apxo_obj_id] = $this->bufferlen;
06260             $this->_out($this->apxo_obj_id.' 0 obj');
06261             $this->_out('<<');
06262             $this->_out('/Type /XObject');
06263             $this->_out('/Subtype /Form');
06264             $this->_out('/FormType 1');
06265             if ($this->compress) {
06266                 $stream = gzcompress($stream);
06267                 $this->_out('/Filter /FlateDecode');
06268             }
06269             $rect = sprintf('%.2F %.2F', $w, $h);
06270             $this->_out('/BBox [0 0 '.$rect.']');
06271             $this->_out('/Matrix [1 0 0 1 0 0]');
06272             $this->_out('/Resources <</ProcSet [/PDF]>>');
06273             $this->_out('/Length '.strlen($stream));
06274             $this->_out('>>');
06275             $this->_putstream($stream);
06276             $this->_out('endobj');
06277             return $this->apxo_obj_id;
06278         }
06279 
06284         protected function _putfonts() {
06285             $nf = $this->n;
06286             foreach ($this->diffs as $diff) {
06287                 //Encodings
06288                 $this->_newobj();
06289                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
06290                 $this->_out('endobj');
06291             }
06292             $mqr = $this->get_mqr();
06293             $this->set_mqr(false);
06294             foreach ($this->FontFiles as $file => $info) {
06295                 // search and get font file to embedd
06296                 $fontdir = $info['fontdir'];
06297                 $file = strtolower($file);
06298                 $fontfile = '';
06299                 // search files on various directories
06300                 if (file_exists($fontdir.$file)) {
06301                     $fontfile = $fontdir.$file;
06302                 } elseif (file_exists($this->_getfontpath().$file)) {
06303                     $fontfile = $this->_getfontpath().$file;
06304                 } elseif (file_exists($file)) {
06305                     $fontfile = $file;
06306                 }
06307                 if (!$this->empty_string($fontfile)) {
06308                     $font = file_get_contents($fontfile);
06309                     $compressed = (substr($file, -2) == '.z');
06310                     if ((!$compressed) AND (isset($info['length2']))) {
06311                         $header = (ord($font{0}) == 128);
06312                         if ($header) {
06313                             //Strip first binary header
06314                             $font = substr($font, 6);
06315                         }
06316                         if ($header AND (ord($font{$info['length1']}) == 128)) {
06317                             //Strip second binary header
06318                             $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
06319                         }
06320                     }
06321                     $this->_newobj();
06322                     $this->FontFiles[$file]['n'] = $this->n;
06323                     $this->_out('<</Length '.strlen($font));
06324                     if ($compressed) {
06325                         $this->_out('/Filter /FlateDecode');
06326                     }
06327                     $this->_out('/Length1 '.$info['length1']);
06328                     if (isset($info['length2'])) {
06329                         $this->_out('/Length2 '.$info['length2'].' /Length3 0');
06330                     }
06331                     $this->_out('>>');
06332                     $this->_putstream($font);
06333                     $this->_out('endobj');
06334                 }
06335             }
06336             $this->set_mqr($mqr);
06337             foreach ($this->fontkeys as $k) {
06338                 //Font objects
06339                 $this->setFontSubBuffer($k, 'n', $this->n + 1);
06340                 $font = $this->getFontBuffer($k);
06341                 $type = $font['type'];
06342                 $name = $font['name'];
06343                 if ($type == 'core') {
06344                     //Standard font
06345                     $obj_id = $this->_newobj();
06346                     $this->_out('<</Type /Font');
06347                     $this->_out('/Subtype /Type1');
06348                     $this->_out('/BaseFont /'.$name);
06349                     $this->_out('/Name /F'.$font['i']);
06350                     if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
06351                         $this->_out('/Encoding /WinAnsiEncoding');
06352                     }
06353                     if (strtolower($name) == 'helvetica') {
06354                         // add default font for annotations
06355                         $this->annotation_fonts['helvetica'] = $k;
06356                     }
06357                     $this->_out('>>');
06358                     $this->_out('endobj');
06359                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
06360                     //Additional Type1 or TrueType font
06361                     $obj_id = $this->_newobj();
06362                     $this->_out('<</Type /Font');
06363                     $this->_out('/Subtype /'.$type);
06364                     $this->_out('/BaseFont /'.$name);
06365                     $this->_out('/Name /F'.$font['i']);
06366                     $this->_out('/FirstChar 32 /LastChar 255');
06367                     $this->_out('/Widths '.($this->n + 1).' 0 R');
06368                     $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
06369                     if ($font['enc']) {
06370                         if (isset($font['diff'])) {
06371                             $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
06372                         } else {
06373                             $this->_out('/Encoding /WinAnsiEncoding');
06374                         }
06375                     }
06376                     $this->_out('>>');
06377                     $this->_out('endobj');
06378                     // Widths
06379                     $this->_newobj();
06380                     $cw = &$font['cw'];
06381                     $s = '[';
06382                     for ($i = 32; $i < 256; ++$i) {
06383                         $s .= $cw[$i].' ';
06384                     }
06385                     $this->_out($s.']');
06386                     $this->_out('endobj');
06387                     //Descriptor
06388                     $this->_newobj();
06389                     $s = '<</Type /FontDescriptor /FontName /'.$name;
06390                     foreach ($font['desc'] as $fdk => $fdv) {
06391                         $s .= ' /'.$fdk.' '.$fdv.'';
06392                     }
06393                     if (!$this->empty_string($font['file'])) {
06394                         $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
06395                     }
06396                     $this->_out($s.'>>');
06397                     $this->_out('endobj');
06398                 } else {
06399                     //Allow for additional types
06400                     $mtd = '_put'.strtolower($type);
06401                     if (!method_exists($this, $mtd)) {
06402                         $this->Error('Unsupported font type: '.$type);
06403                     }
06404                     $obj_id = $this->$mtd($font);
06405                 }
06406                 // store object ID for current font
06407                 $this->font_obj_ids[$k] = $obj_id;
06408             }
06409         }
06410         
06419         protected function _putfontwidths($font, $cidoffset=0) {
06420             ksort($font['cw']);
06421             $rangeid = 0;
06422             $range = array();
06423             $prevcid = -2;
06424             $prevwidth = -1;
06425             $interval = false;
06426             // for each character
06427             foreach ($font['cw'] as $cid => $width) {
06428                 $cid -= $cidoffset;
06429                 if ($width != $font['dw']) {
06430                     if ($cid == ($prevcid + 1)) {
06431                         // consecutive CID
06432                         if ($width == $prevwidth) {
06433                             if ($width == $range[$rangeid][0]) {
06434                                 $range[$rangeid][] = $width;
06435                             } else {
06436                                 array_pop($range[$rangeid]);
06437                                 // new range
06438                                 $rangeid = $prevcid;
06439                                 $range[$rangeid] = array();
06440                                 $range[$rangeid][] = $prevwidth;
06441                                 $range[$rangeid][] = $width;
06442                             }
06443                             $interval = true;
06444                             $range[$rangeid]['interval'] = true;
06445                         } else {
06446                             if ($interval) {
06447                                 // new range
06448                                 $rangeid = $cid;
06449                                 $range[$rangeid] = array();
06450                                 $range[$rangeid][] = $width;
06451                             } else {
06452                                 $range[$rangeid][] = $width;
06453                             }
06454                             $interval = false;
06455                         }
06456                     } else {
06457                         // new range
06458                         $rangeid = $cid;
06459                         $range[$rangeid] = array();
06460                         $range[$rangeid][] = $width;
06461                         $interval = false;
06462                     }
06463                     $prevcid = $cid;
06464                     $prevwidth = $width;
06465                 }
06466             }
06467             // optimize ranges
06468             $prevk = -1;
06469             $nextk = -1;
06470             $prevint = false;
06471             foreach ($range as $k => $ws) {
06472                 $cws = count($ws);
06473                 if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
06474                     if (isset($range[$k]['interval'])) {
06475                         unset($range[$k]['interval']);
06476                     }
06477                     $range[$prevk] = array_merge($range[$prevk], $range[$k]);
06478                     unset($range[$k]);
06479                 } else {
06480                     $prevk = $k;
06481                 }
06482                 $nextk = $k + $cws;
06483                 if (isset($ws['interval'])) {
06484                     if ($cws > 3) {
06485                         $prevint = true;
06486                     } else {
06487                         $prevint = false;
06488                     }
06489                     unset($range[$k]['interval']);
06490                     --$nextk;
06491                 } else {
06492                     $prevint = false;
06493                 }
06494             }
06495             // output data
06496             $w = '';
06497             foreach ($range as $k => $ws) {
06498                 if (count(array_count_values($ws)) == 1) {
06499                     // interval mode is more compact
06500                     $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
06501                 } else {
06502                     // range mode
06503                     $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
06504                 }
06505             }
06506             $this->_out('/W ['.$w.' ]');
06507         }
06508         
06518         protected function _puttruetypeunicode($font) {
06519             // Type0 Font
06520             // A composite font composed of other fonts, organized hierarchically
06521             $obj_id = $this->_newobj();
06522             $this->_out('<</Type /Font');
06523             $this->_out('/Subtype /Type0');
06524             $this->_out('/BaseFont /'.$font['name'].'');
06525             $this->_out('/Name /F'.$font['i']);
06526             $this->_out('/Encoding /'.$font['enc']);
06527             $this->_out('/ToUnicode /Identity-H');
06528             $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
06529             $this->_out('>>');
06530             $this->_out('endobj');
06531             // CIDFontType2
06532             // A CIDFont whose glyph descriptions are based on TrueType font technology
06533             $this->_newobj();
06534             $this->_out('<</Type /Font');
06535             $this->_out('/Subtype /CIDFontType2');
06536             $this->_out('/BaseFont /'.$font['name'].'');
06537             // A dictionary containing entries that define the character collection of the CIDFont.
06538             $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
06539             $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
06540             $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
06541             $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
06542             $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
06543             $this->_out('/DW '.$font['dw'].''); // default width
06544             $this->_putfontwidths($font, 0);
06545             $this->_out('/CIDToGIDMap '.($this->n + 2).' 0 R');
06546             $this->_out('>>');
06547             $this->_out('endobj');          
06548             // Font descriptor
06549             // A font descriptor describing the CIDFont default metrics other than its glyph widths
06550             $this->_newobj();
06551             $this->_out('<</Type /FontDescriptor');
06552             $this->_out('/FontName /'.$font['name']);
06553             foreach ($font['desc'] as $key => $value) {
06554                 $this->_out('/'.$key.' '.$value);
06555             }
06556             $fontdir = '';
06557             if (!$this->empty_string($font['file'])) {
06558                 // A stream containing a TrueType font
06559                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
06560                 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
06561             }
06562             $this->_out('>>');
06563             $this->_out('endobj');
06564             $this->_newobj();
06565             if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
06566                 // Embed CIDToGIDMap
06567                 // A specification of the mapping from CIDs to glyph indices
06568                 // search and get CTG font file to embedd
06569                 $ctgfile = strtolower($font['ctg']);
06570                 // search and get ctg font file to embedd
06571                 $fontfile = '';
06572                 // search files on various directories
06573                 if (file_exists($fontdir.$ctgfile)) {
06574                     $fontfile = $fontdir.$ctgfile;
06575                 } elseif (file_exists($this->_getfontpath().$ctgfile)) {
06576                     $fontfile = $this->_getfontpath().$ctgfile;
06577                 } elseif (file_exists($ctgfile)) {
06578                     $fontfile = $ctgfile;
06579                 }
06580                 if ($this->empty_string($fontfile)) {
06581                     $this->Error('Font file not found: '.$ctgfile);
06582                 }
06583                 $size = filesize($fontfile);
06584                 $this->_out('<</Length '.$size.'');
06585                 if (substr($fontfile, -2) == '.z') { // check file extension
06586                     // Decompresses data encoded using the public-domain 
06587                     // zlib/deflate compression method, reproducing the 
06588                     // original text or binary data
06589                     $this->_out('/Filter /FlateDecode');
06590                 }
06591                 $this->_out('>>');
06592                 $this->_putstream(file_get_contents($fontfile));
06593             }
06594             $this->_out('endobj');
06595             return $obj_id;
06596         }
06597         
06607         protected function _putcidfont0($font) {
06608             $cidoffset = 0;
06609             if (!isset($font['cw'][1])) {
06610                 $cidoffset = 31;
06611             }
06612             if (isset($font['cidinfo']['uni2cid'])) {
06613                 // convert unicode to cid.
06614                 $uni2cid = $font['cidinfo']['uni2cid'];
06615                 $cw = array();
06616                 foreach ($font['cw'] as $uni => $width) {
06617                     if (isset($uni2cid[$uni])) {
06618                         $cw[($uni2cid[$uni] + $cidoffset)] = $width;
06619                     } elseif ($uni < 256) {
06620                         $cw[$uni] = $width;
06621                     } // else unknown character
06622                 }
06623                 $font = array_merge($font, array('cw' => $cw));
06624             }
06625             $name = $font['name'];
06626             $enc = $font['enc'];
06627             if ($enc) {
06628                 $longname = $name.'-'.$enc;
06629             } else {
06630                 $longname = $name;
06631             }
06632             $obj_id = $this->_newobj();
06633             $this->_out('<</Type /Font');
06634             $this->_out('/Subtype /Type0');
06635             $this->_out('/BaseFont /'.$longname);
06636             $this->_out('/Name /F'.$font['i']);
06637             if ($enc) {
06638                 $this->_out('/Encoding /'.$enc);
06639             }
06640             $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
06641             $this->_out('>>');
06642             $this->_out('endobj');
06643             $this->_newobj();
06644             $this->_out('<</Type /Font');
06645             $this->_out('/Subtype /CIDFontType0');
06646             $this->_out('/BaseFont /'.$name);
06647             $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
06648             $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
06649             $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
06650             $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
06651             $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
06652             $this->_out('/DW '.$font['dw']);
06653             $this->_putfontwidths($font, $cidoffset);
06654             $this->_out('>>');
06655             $this->_out('endobj');
06656             $this->_newobj();
06657             $s = '<</Type /FontDescriptor /FontName /'.$name;
06658             foreach ($font['desc'] as $k => $v) {
06659                 if ($k != 'Style') {
06660                     $s .= ' /'.$k.' '.$v.'';
06661                 }
06662             }
06663             $this->_out($s.'>>');
06664             $this->_out('endobj');
06665             return $obj_id;
06666         }
06667 
06672         protected function _putimages() {
06673             $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
06674             foreach ($this->imagekeys as $file) {
06675                 $info = $this->getImageBuffer($file);
06676                 $this->_newobj();
06677                 $this->setImageSubBuffer($file, 'n', $this->n);
06678                 $this->_out('<</Type /XObject');
06679                 $this->_out('/Subtype /Image');
06680                 $this->_out('/Width '.$info['w']);
06681                 $this->_out('/Height '.$info['h']);
06682                 if (isset($info['masked'])) {
06683                     $this->_out('/SMask '.($this->n - 1).' 0 R');
06684                 }
06685                 if ($info['cs'] == 'Indexed') {
06686                     $this->_out('/ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]');
06687                 } else {
06688                     $this->_out('/ColorSpace /'.$info['cs']);
06689                     if ($info['cs'] == 'DeviceCMYK') {
06690                         $this->_out('/Decode [1 0 1 0 1 0 1 0]');
06691                     }
06692                 }
06693                 $this->_out('/BitsPerComponent '.$info['bpc']);
06694                 if (isset($info['f'])) {
06695                     $this->_out('/Filter /'.$info['f']);
06696                 }
06697                 if (isset($info['parms'])) {
06698                     $this->_out($info['parms']);
06699                 }
06700                 if (isset($info['trns']) AND is_array($info['trns'])) {
06701                     $trns='';
06702                     $count_info = count($info['trns']);
06703                     for ($i=0; $i < $count_info; ++$i) {
06704                         $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
06705                     }
06706                     $this->_out('/Mask ['.$trns.']');
06707                 }
06708                 $this->_out('/Length '.strlen($info['data']).'>>');
06709                 $this->_putstream($info['data']);
06710                 $this->_out('endobj');
06711                 //Palette
06712                 if ($info['cs'] == 'Indexed') {
06713                     $this->_newobj();
06714                     $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
06715                     $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
06716                     $this->_putstream($pal);
06717                     $this->_out('endobj');
06718                 }
06719             }
06720         }
06721 
06727         protected function _putspotcolors() {
06728             foreach ($this->spot_colors as $name => $color) {
06729                 $this->_newobj();
06730                 $this->spot_colors[$name]['n'] = $this->n;
06731                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
06732                 $this->_out('/DeviceCMYK <<');
06733                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
06734                 $this->_out(sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
06735                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
06736                 $this->_out('endobj');
06737             }
06738         }
06739 
06744         protected function _putxobjectdict() {
06745             foreach ($this->imagekeys as $file) {
06746                 $info = $this->getImageBuffer($file);
06747                 $this->_out('/I'.$info['i'].' '.$info['n'].' 0 R');
06748             }
06749         }
06750 
06755         protected function _putresourcedict() {
06756             $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
06757             $this->_out('/Font <<');
06758             foreach ($this->fontkeys as $fontkey) {
06759                 $font = $this->getFontBuffer($fontkey);
06760                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
06761             }
06762             $this->_out('>>');
06763             $this->_out('/XObject <<');
06764             $this->_putxobjectdict();
06765             $this->_out('>>');
06766             // visibility
06767             $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
06768             // transparency
06769             $this->_out('/ExtGState <<');
06770             foreach ($this->extgstates as $k => $extgstate) {
06771                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
06772             }
06773             $this->_out('>>');
06774             // gradients
06775             if (isset($this->gradients) AND (count($this->gradients) > 0)) {
06776                 $this->_out('/Shading <<');
06777                 foreach ($this->gradients as $id => $grad) {
06778                     $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
06779                 }
06780                 $this->_out('>>');
06781             }
06782             // spot colors
06783             if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
06784                 $this->_out('/ColorSpace <<');
06785                 foreach ($this->spot_colors as $color) {
06786                     $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
06787                 }
06788                 $this->_out('>>');
06789             }
06790         }
06791         
06796         protected function _putresources() {
06797             $this->_putextgstates();
06798             $this->_putocg();
06799             $this->_putfonts();
06800             $this->_putimages();
06801             $this->_putspotcolors();
06802             $this->_putshaders();
06803             //Resource dictionary
06804             $this->offsets[2] = $this->bufferlen;
06805             $this->_out('2 0 obj');
06806             $this->_out('<<');
06807             $this->_putresourcedict();
06808             $this->_out('>>');
06809             $this->_out('endobj');
06810             $this->_putbookmarks();
06811             $this->_putEmbeddedFiles();
06812             $this->_putannotsobjs();
06813             $this->_putjavascript();
06814             // encryption
06815             if ($this->encrypted) {
06816                 $this->_newobj();
06817                 $this->enc_obj_id = $this->n;
06818                 $this->_out('<<');
06819                 $this->_putencryption();
06820                 $this->_out('>>');
06821                 $this->_out('endobj');
06822             }
06823         }
06824         
06830         protected function _putinfo() {
06831             if ($this->empty_string($this->title)) {
06832                 $this->title = '?';
06833             }
06834             $this->_out('/Title '.$this->_textstring($this->title));
06835             if ($this->empty_string($this->author)) {
06836                 $this->author = '?';
06837             }
06838             $this->_out('/Author '.$this->_textstring($this->author));
06839             if ($this->empty_string($this->subject)) {
06840                 $this->subject = '?';
06841             }
06842             $this->_out('/Subject '.$this->_textstring($this->subject));
06843             if ($this->empty_string($this->keywords)) {
06844                 $this->keywords = '?';
06845             }
06846             $this->_out('/Keywords '.$this->_textstring($this->keywords));
06847             if ($this->empty_string($this->creator)) {
06848                 $this->creator = '?';
06849             }
06850             $this->_out('/Creator '.$this->_textstring($this->creator));
06851             if (defined('PDF_PRODUCER')) {
06852                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
06853             } else {
06854                 $this->_out('/Producer '.$this->_textstring('TCPDF'));
06855             }
06856             $this->_out('/CreationDate '.$this->_datestring());
06857             $this->_out('/ModDate '.$this->_datestring());  
06858         }
06859         
06864         protected function _putcatalog() {
06865             $this->_out('/Type /Catalog');
06866             $this->_out('/Pages 1 0 R');
06867             if ($this->ZoomMode == 'fullpage') {
06868                 $this->_out('/OpenAction [3 0 R /Fit]');
06869             } elseif ($this->ZoomMode == 'fullwidth') {
06870                 $this->_out('/OpenAction [3 0 R /FitH null]');
06871             } elseif ($this->ZoomMode == 'real') {
06872                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
06873             } elseif (!is_string($this->ZoomMode)) {
06874                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
06875             }           
06876             if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
06877                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
06878             }
06879             if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
06880                 $this->_out('/PageMode /'.$this->PageMode);
06881             }
06882             if (isset($this->l['a_meta_language'])) {
06883                 $this->_out('/Lang /'.$this->l['a_meta_language']);
06884             }
06885             $this->_out('/Names <<');
06886             if ((!empty($this->javascript)) OR (!empty($this->js_objects))) {
06887                 $this->_out('/JavaScript '.($this->n_js).' 0 R');
06888             }
06889             $this->_out('>>');          
06890             if (count($this->outlines) > 0) {
06891                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
06892                 $this->_out('/PageMode /UseOutlines');
06893             }
06894             $this->_putviewerpreferences();
06895             $p = $this->n_ocg_print.' 0 R';
06896             $v = $this->n_ocg_view.' 0 R';
06897             $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>';
06898             $this->_out('/OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>');
06899             // AcroForm
06900             if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
06901                 $this->_out('/AcroForm<<');
06902                 $objrefs = '';
06903                 if ($this->sign AND isset($this->signature_data['cert_type'])) {
06904                     $objrefs .= $this->sig_obj_id.' 0 R';
06905                 }
06906                 if (!empty($this->form_obj_id)) {
06907                     foreach($this->form_obj_id as $objid) {
06908                         $objrefs .= ' '.$objid.' 0 R';
06909                     }
06910                 }
06911                 $this->_out('/Fields ['.$objrefs.']');
06912                 $this->_out('/NeedAppearances '.(empty($this->form_obj_id)?'false':'true'));
06913                 if ($this->sign AND isset($this->signature_data['cert_type'])) {
06914                     $this->_out('/SigFlags 3');
06915                 }
06916                 //$this->_out('/CO ');
06917                 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
06918                     $this->_out('/DR <<');
06919                     $this->_out('/Font <<');
06920                     foreach ($this->annotation_fonts as $font => $fontkey) {
06921                         $this->_out('/F'.($fontkey + 1).' '.$this->font_obj_ids[$font].' 0 R');
06922                     }
06923                     $this->_out('>>');
06924                     $this->_out('>>');
06925                 }
06926                 $this->_out('/DA (/F'.(array_search('helvetica', $this->fontkeys) + 1).' 0 Tf 0 g)');
06927                 $this->_out('/Q '.(($this->rtl)?'2':'0'));
06928                 //$this->_out('/XFA ');
06929                 $this->_out('>>');
06930                 // signatures
06931                 if ($this->sign AND isset($this->signature_data['cert_type'])) {
06932                     if ($this->signature_data['cert_type'] > 0) {
06933                         $this->_out('/Perms<</DocMDP '.($this->sig_obj_id + 1).' 0 R>>');
06934                     } else {
06935                         $this->_out('/Perms<</UR3 '.($this->sig_obj_id + 1).' 0 R>>');
06936                     }
06937                 }
06938             }
06939         }
06940         
06947         protected function _putviewerpreferences() {
06948             $this->_out('/ViewerPreferences<<');
06949             if ($this->rtl) {
06950                 $this->_out('/Direction /R2L');
06951             } else {
06952                 $this->_out('/Direction /L2R');
06953             }
06954             if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
06955                 $this->_out('/HideToolbar true');
06956             }
06957             if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
06958                 $this->_out('/HideMenubar true');
06959             }
06960             if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
06961                 $this->_out('/HideWindowUI true');
06962             }
06963             if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
06964                 $this->_out('/FitWindow true');
06965             }
06966             if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
06967                 $this->_out('/CenterWindow true');
06968             }
06969             if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
06970                 $this->_out('/DisplayDocTitle true');
06971             }
06972             if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
06973                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
06974             }
06975             if (isset($this->viewer_preferences['ViewArea'])) {
06976                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
06977             }
06978             if (isset($this->viewer_preferences['ViewClip'])) {
06979                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
06980             }
06981             if (isset($this->viewer_preferences['PrintArea'])) {
06982                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
06983             }
06984             if (isset($this->viewer_preferences['PrintClip'])) {
06985                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
06986             }
06987             if (isset($this->viewer_preferences['PrintScaling'])) {
06988                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
06989             }
06990             if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
06991                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
06992             }
06993             if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
06994                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
06995                     $this->_out('/PickTrayByPDFSize true');
06996                 } else {
06997                     $this->_out('/PickTrayByPDFSize false');
06998                 }
06999             }
07000             if (isset($this->viewer_preferences['PrintPageRange'])) {
07001                 $PrintPageRangeNum = '';
07002                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
07003                     $PrintPageRangeNum .= ' '.($v - 1).'';
07004                 }
07005                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
07006             }
07007             if (isset($this->viewer_preferences['NumCopies'])) {
07008                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
07009             }
07010             $this->_out('>>');
07011         }
07012         
07017         protected function _puttrailer() {
07018             $this->_out('/Size '.($this->n + 1));
07019             $this->_out('/Root '.$this->n.' 0 R');
07020             $this->_out('/Info '.($this->n - 1).' 0 R');
07021             if ($this->encrypted) {
07022                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
07023                 $this->_out('/ID [()()]');
07024             }
07025         }
07026 
07031         protected function _putheader() {
07032             $this->_out('%PDF-'.$this->PDFVersion);
07033         }
07034 
07039         protected function _enddoc() {
07040             $this->state = 1;
07041             $this->_putheader();            
07042             $this->_putpages();
07043             $this->_putresources();
07044             // Signature
07045             if ($this->sign AND isset($this->signature_data['cert_type'])) {
07046                 // widget annotation for signature
07047                 $this->sig_obj_id = $this->_newobj();
07048                 // --- replace signature ID on the first page ---
07049                 // get the document content
07050                 $pdfdoc = $this->getBuffer();
07051                 // Remove the original buffer
07052                 if (isset($this->diskcache) AND $this->diskcache) {
07053                     // remove buffer file from cache
07054                     unlink($this->buffer);
07055                 }
07056                 unset($this->buffer);
07057                 $signature_widget_ref = sprintf('%u 0 R', $this->sig_obj_id);
07058                 $signature_widget_ref .= str_repeat(' ', (strlen($this->sig_annot_ref) - strlen($signature_widget_ref)));
07059                 $pdfdoc = str_replace($this->sig_annot_ref, $signature_widget_ref, $pdfdoc);
07060                 $this->diskcache = false;
07061                 $this->buffer = &$pdfdoc;
07062                 $this->bufferlen = strlen($pdfdoc);
07063                 // ---
07064                 $this->_out('<<');
07065                 $this->_out('/Type /Annot /Subtype /Widget /Rect [0 0 0 0]');
07066                 $this->_out('/P 3 0 R'); // link to first page object
07067                 $this->_out('/FT /Sig');
07068                 $this->_out('/T '.$this->_textstring('Signature'));
07069                 $this->_out('/Ff 0');
07070                 $this->_out('/V '.($this->sig_obj_id + 1).' 0 R');
07071                 $this->_out('>>');
07072                 $this->_out('endobj');
07073                 // signature        
07074                 $this->_newobj();
07075                 $this->_out('<<');
07076                 $this->_putsignature();
07077                 $this->_out('>>');
07078                 $this->_out('endobj');
07079             }
07080             // Info
07081             $this->_newobj();
07082             $this->_out('<<');
07083             $this->_putinfo();
07084             $this->_out('>>');
07085             $this->_out('endobj');
07086             // Catalog
07087             $this->_newobj();
07088             $this->_out('<<');
07089             $this->_putcatalog();
07090             $this->_out('>>');
07091             $this->_out('endobj');
07092             // Cross-ref
07093             $o = $this->bufferlen;
07094             $this->_out('xref');
07095             $this->_out('0 '.($this->n + 1));
07096             $this->_out('0000000000 65535 f ');
07097             for ($i=1; $i <= $this->n; ++$i) {
07098                 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
07099             }
07100             // Embedded Files
07101             if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) {
07102                 $this->_out($this->embedded_start_obj_id.' '.count($this->embeddedfiles));
07103                 foreach ($this->embeddedfiles as $filename => $filedata) {
07104                     $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']]));
07105                 }
07106             }
07107             // Annotation Objects
07108             if ($this->annot_obj_id > $this->annots_start_obj_id) {
07109                 $this->_out(($this->annots_start_obj_id + 1).' '.($this->annot_obj_id - $this->annots_start_obj_id));
07110                 for ($i = ($this->annots_start_obj_id + 1); $i <= $this->annot_obj_id; ++$i) {
07111                     $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
07112                 }
07113             }
07114             // Javascript Objects
07115             if ($this->js_obj_id > $this->js_start_obj_id) {
07116                 $this->_out(($this->js_start_obj_id + 1).' '.($this->js_obj_id - $this->js_start_obj_id));
07117                 for ($i = ($this->js_start_obj_id + 1); $i <= $this->js_obj_id; ++$i) {
07118                     $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
07119                 }
07120             }
07121             // Appearance streams XObjects
07122             if ($this->apxo_obj_id > $this->apxo_start_obj_id) {
07123                 $this->_out(($this->apxo_start_obj_id + 1).' '.($this->apxo_obj_id - $this->apxo_start_obj_id));
07124                 for ($i = ($this->apxo_start_obj_id + 1); $i <= $this->apxo_obj_id; ++$i) {
07125                     $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
07126                 }
07127             }
07128             //Trailer
07129             $this->_out('trailer');
07130             $this->_out('<<');
07131             $this->_puttrailer();
07132             $this->_out('>>');
07133             $this->_out('startxref');
07134             $this->_out($o);
07135             $this->_out('%%EOF');
07136             $this->state = 3; // end-of-doc
07137             if ($this->diskcache) {
07138                 // remove temporary files used for images
07139                 foreach ($this->imagekeys as $key) {
07140                     // remove temporary files
07141                     unlink($this->images[$key]);
07142                 }
07143                 foreach ($this->fontkeys as $key) {
07144                     // remove temporary files
07145                     unlink($this->fonts[$key]);
07146                 }
07147             }
07148         }
07149 
07156         protected function _beginpage($orientation='', $format='') {
07157             ++$this->page;
07158             $this->setPageBuffer($this->page, '');
07159             // initialize array for graphics tranformation positions inside a page buffer
07160             $this->transfmrk[$this->page] = array();
07161             $this->state = 2;
07162             if ($this->empty_string($orientation)) {
07163                 if (isset($this->CurOrientation)) {
07164                     $orientation = $this->CurOrientation;
07165                 } else {
07166                     $orientation = 'P';
07167                 }
07168             }
07169             if ($this->empty_string($format)) {
07170                 $this->setPageOrientation($orientation);
07171             } else {
07172                 $this->setPageFormat($format, $orientation);
07173             }
07174             if ($this->rtl) {
07175                 $this->x = $this->w - $this->rMargin;
07176             } else {
07177                 $this->x = $this->lMargin;
07178             }
07179             $this->y = $this->tMargin;
07180             if (isset($this->newpagegroup[$this->page])) {
07181                 // start a new group
07182                 $n = sizeof($this->pagegroups) + 1;
07183                 $alias = '{nb'.$n.'}';
07184                 $this->pagegroups[$alias] = 1;
07185                 $this->currpagegroup = $alias;
07186             } elseif ($this->currpagegroup) {
07187                 ++$this->pagegroups[$this->currpagegroup];
07188             }
07189         }
07190 
07195         protected function _endpage() {
07196             $this->setVisibility('all');
07197             $this->state = 1;
07198         }
07199 
07205         protected function _newobj() {
07206             ++$this->n;
07207             $this->offsets[$this->n] = $this->bufferlen;
07208             $this->_out($this->n.' 0 obj');
07209             return $this->n;
07210         }
07211 
07219         protected function _dounderline($x, $y, $txt) {
07220             $up = $this->CurrentFont['up'];
07221             $ut = $this->CurrentFont['ut'];
07222             $w = $this->GetStringWidth($txt);
07223             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);
07224         }
07225         
07233         protected function _dolinethrough($x, $y, $txt) {
07234             $up = $this->CurrentFont['up'];
07235             $ut = $this->CurrentFont['ut'];
07236             $w = $this->GetStringWidth($txt);
07237             return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - ($this->FontSize/2) - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
07238         }
07239         
07246         protected function _freadint($f) {
07247             $a = unpack('Ni', fread($f, 4));
07248             return $a['i'];
07249         }
07250         
07257         protected function _escape($s) {
07258             // the chr(13) substitution fixes the Bugs item #1421290.
07259             return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
07260         }
07261         
07268         protected function _datastring($s) {
07269             if ($this->encrypted) {
07270                 $s = $this->_RC4($this->_objectkey($this->n), $s);
07271             }
07272             return '('. $this->_escape($s).')';
07273         }
07274 
07281         protected function _datestring() {
07282             $current_time = substr_replace(date('YmdHisO'), '\'', (0 - 2), 0).'\'';
07283             return $this->_datastring('D:'.$current_time);
07284         }
07285 
07292         protected function _textstring($s) {
07293             if ($this->isunicode) {
07294                 //Convert string to UTF-16BE
07295                 $s = $this->UTF8ToUTF16BE($s, true);
07296             }
07297             return $this->_datastring($s);
07298         }
07299                 
07306         protected function _escapetext($s) {
07307             if ($this->isunicode) {
07308                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
07309                     $s = $this->UTF8ToLatin1($s);
07310                 } else {
07311                     //Convert string to UTF-16BE and reverse RTL language
07312                     $s = $this->utf8StrRev($s, false, $this->tmprtl);
07313                 }
07314             }
07315             return $this->_escape($s);
07316         }
07317         
07323         protected function _putstream($s) {
07324             if ($this->encrypted) {
07325                 $s = $this->_RC4($this->_objectkey($this->n), $s);
07326             }
07327             $this->_out('stream');
07328             $this->_out($s);
07329             $this->_out('endstream');
07330         }
07331         
07337         protected function _out($s) {
07338             if ($this->state == 2) {
07339                 if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
07340                     // puts data before page footer
07341                     $pagebuff = $this->getPageBuffer($this->page);
07342                     $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
07343                     $footer = substr($pagebuff, -$this->footerlen[$this->page]);
07344                     $this->setPageBuffer($this->page, $page.$s."\n".$footer);
07345                     // update footer position
07346                     $this->footerpos[$this->page] += strlen($s."\n");   
07347                 } else {
07348                     $this->setPageBuffer($this->page, $s."\n", true);
07349                 }
07350             } else {
07351                 $this->setBuffer($s."\n");
07352             }
07353         }
07354         
07389         protected function UTF8StringToArray($str) {
07390             if (isset($this->cache_UTF8StringToArray['_'.$str])) {
07391                 // return cached value
07392                 return($this->cache_UTF8StringToArray['_'.$str]);
07393             }
07394             // check cache size
07395             if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
07396                 // remove first element
07397                 array_shift($this->cache_UTF8StringToArray);
07398             }
07399             ++$this->cache_size_UTF8StringToArray;
07400             if (!$this->isunicode) {
07401                 // split string into array of equivalent codes
07402                 $strarr = array();
07403                 $strlen = strlen($str);
07404                 for ($i=0; $i < $strlen; ++$i) {
07405                     $strarr[] = ord($str{$i});
07406                 }
07407                 // insert new value on cache
07408                 $this->cache_UTF8StringToArray['_'.$str] = $strarr;
07409                 return $strarr;
07410             }
07411             $unicode = array(); // array containing unicode values
07412             $bytes  = array(); // array containing single character byte sequences
07413             $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
07414             $str .= ''; // force $str to be a string
07415             $length = strlen($str);
07416             for ($i = 0; $i < $length; ++$i) {
07417                 $char = ord($str{$i}); // get one string character at time
07418                 if (count($bytes) == 0) { // get starting octect
07419                     if ($char <= 0x7F) {
07420                         $unicode[] = $char; // use the character "as is" because is ASCII
07421                         $numbytes = 1;
07422                     } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
07423                         $bytes[] = ($char - 0xC0) << 0x06; 
07424                         $numbytes = 2;
07425                     } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
07426                         $bytes[] = ($char - 0xE0) << 0x0C; 
07427                         $numbytes = 3;
07428                     } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
07429                         $bytes[] = ($char - 0xF0) << 0x12; 
07430                         $numbytes = 4;
07431                     } else {
07432                         // use replacement character for other invalid sequences
07433                         $unicode[] = 0xFFFD;
07434                         $bytes = array();
07435                         $numbytes = 1;
07436                     }
07437                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
07438                     $bytes[] = $char - 0x80;
07439                     if (count($bytes) == $numbytes) {
07440                         // compose UTF-8 bytes to a single unicode value
07441                         $char = $bytes[0];
07442                         for ($j = 1; $j < $numbytes; ++$j) {
07443                             $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
07444                         }
07445                         if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
07446                             /* The definition of UTF-8 prohibits encoding character numbers between
07447                             U+D800 and U+DFFF, which are reserved for use with the UTF-16
07448                             encoding form (as surrogate pairs) and do not directly represent
07449                             characters. */
07450                             $unicode[] = 0xFFFD; // use replacement character
07451                         } else {
07452                             $unicode[] = $char; // add char to array
07453                         }
07454                         // reset data for next char
07455                         $bytes = array(); 
07456                         $numbytes = 1;
07457                     }
07458                 } else {
07459                     // use replacement character for other invalid sequences
07460                     $unicode[] = 0xFFFD;
07461                     $bytes = array();
07462                     $numbytes = 1;
07463                 }
07464             }
07465             // insert new value on cache
07466             $this->cache_UTF8StringToArray['_'.$str] = $unicode;
07467             return $unicode;
07468         }
07469         
07480         protected function UTF8ToUTF16BE($str, $setbom=true) {
07481             if (!$this->isunicode) {
07482                 return $str; // string is not in unicode
07483             }
07484             $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
07485             return $this->arrUTF8ToUTF16BE($unicode, $setbom);
07486         }
07487         
07496         protected function UTF8ToLatin1($str) {
07497             global $utf8tolatin;
07498             if (!$this->isunicode) {
07499                 return $str; // string is not in unicode
07500             }
07501             $outstr = ''; // string to be returned
07502             $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
07503             foreach ($unicode as $char) {
07504                 if ($char < 256) {
07505                     $outstr .= chr($char);
07506                 } elseif (array_key_exists($char, $utf8tolatin)) {
07507                     // map from UTF-8
07508                     $outstr .= chr($utf8tolatin[$char]);
07509                 } elseif ($char == 0xFFFD) {
07510                     // skip
07511                 } else {
07512                     $outstr .= '?';
07513                 }
07514             }
07515             return $outstr;
07516         }
07517 
07556         protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
07557             $outstr = ''; // string to be returned
07558             if ($setbom) {
07559                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
07560             }
07561             foreach ($unicode as $char) {
07562                 if ($char == 0xFFFD) {
07563                     $outstr .= "\xFF\xFD"; // replacement character
07564                 } elseif ($char < 0x10000) {
07565                     $outstr .= chr($char >> 0x08);
07566                     $outstr .= chr($char & 0xFF);
07567                 } else {
07568                     $char -= 0x10000;
07569                     $w1 = 0xD800 | ($char >> 0x10);
07570                     $w2 = 0xDC00 | ($char & 0x3FF); 
07571                     $outstr .= chr($w1 >> 0x08);
07572                     $outstr .= chr($w1 & 0xFF);
07573                     $outstr .= chr($w2 >> 0x08);
07574                     $outstr .= chr($w2 & 0xFF);
07575                 }
07576             }
07577             return $outstr;
07578         }
07579         // ====================================================
07580         
07587         public function setHeaderFont($font) {
07588             $this->header_font = $font;
07589         }
07590         
07597         public function getHeaderFont() {
07598             return $this->header_font;
07599         }
07600         
07607         public function setFooterFont($font) {
07608             $this->footer_font = $font;
07609         }
07610         
07617         public function getFooterFont() {
07618             return $this->footer_font;
07619         }
07620         
07627         public function setLanguageArray($language) {
07628             $this->l = $language;
07629             if (isset($this->l['a_meta_dir'])) {
07630                 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
07631             } else {
07632                 $this->rtl = false;
07633             }
07634         }
07635         
07640         public function getPDFData() {
07641             if ($this->state < 3) {
07642                 $this->Close();
07643             }
07644             return $this->buffer;
07645         }
07646                 
07658         public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) {
07659             if (!$this->empty_string($url) AND ($url{0} == '#')) {
07660                 // convert url to internal link
07661                 $page = intval(substr($url, 1));
07662                 $url = $this->AddLink();
07663                 $this->SetLink($url, 0, $page);
07664             }
07665             // store current settings
07666             $prevcolor = $this->fgcolor;
07667             $prevstyle = $this->FontStyle;
07668             if (empty($color)) {
07669                 $this->SetTextColorArray($this->htmlLinkColorArray);
07670             } else {
07671                 $this->SetTextColorArray($color);
07672             }
07673             if ($style == -1) {
07674                 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
07675             } else {
07676                 $this->SetFont('', $this->FontStyle.$style);
07677             }
07678             $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
07679             // restore settings
07680             $this->SetFont('', $prevstyle);
07681             $this->SetTextColorArray($prevcolor);
07682             return $ret;
07683         }
07684         
07691         public function convertHTMLColorToDec($color='#FFFFFF') {
07692             global $webcolor;
07693             $returncolor = false;
07694             $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
07695             $color = strtolower($color);
07696             if (($dotpos = strpos($color, '.')) !== false) {
07697                 // remove class parent (i.e.: color.red)
07698                 $color = substr($color, ($dotpos + 1));
07699             }
07700             if (strlen($color) == 0) {
07701                 return false;
07702             }
07703             if (substr($color, 0, 3) == 'rgb') {
07704                 $codes = substr($color, 4);
07705                 $codes = str_replace(')', '', $codes);
07706                 $returncolor = explode(',', $codes, 3);
07707                 return $returncolor;
07708             }
07709             if (substr($color, 0, 1) != '#') {
07710                 // decode color name
07711                 if (isset($webcolor[$color])) {
07712                     $color_code = $webcolor[$color];
07713                 } else {
07714                     return false;
07715                 }
07716             } else {
07717                 $color_code = substr($color, 1);
07718             }
07719             switch (strlen($color_code)) {
07720                 case 3: {
07721                     // three-digit hexadecimal representation
07722                     $r = substr($color_code, 0, 1);
07723                     $g = substr($color_code, 1, 1);
07724                     $b = substr($color_code, 2, 1);
07725                     $returncolor['R'] = hexdec($r.$r);
07726                     $returncolor['G'] = hexdec($g.$g);
07727                     $returncolor['B'] = hexdec($b.$b);
07728                     break;
07729                 }
07730                 case 6: {
07731                     // six-digit hexadecimal representation
07732                     $returncolor['R'] = hexdec(substr($color_code, 0, 2));
07733                     $returncolor['G'] = hexdec(substr($color_code, 2, 2));
07734                     $returncolor['B'] = hexdec(substr($color_code, 4, 2));
07735                     break;
07736                 }
07737             }
07738             return $returncolor;
07739         }
07740         
07748         public function pixelsToUnits($px) {
07749             return ($px / ($this->imgscale * $this->k));
07750         }
07751             
07759         public function unhtmlentities($text_to_convert) {
07760             return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
07761         }
07762         
07763         // ENCRYPTION METHODS ----------------------------------
07764         // SINCE 2.0.000 (2008-01-02)
07765         
07772         protected function _objectkey($n) {
07773             return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10);
07774         }
07775         
07781         protected function _putencryption() {
07782             $this->_out('/Filter /Standard');
07783             $this->_out('/V 1');
07784             $this->_out('/R 2');
07785             $this->_out('/O ('.$this->_escape($this->Ovalue).')');
07786             $this->_out('/U ('.$this->_escape($this->Uvalue).')');
07787             $this->_out('/P '.$this->Pvalue);
07788         }
07789         
07800         protected function _RC4($key, $text) {
07801             if ($this->last_rc4_key != $key) {
07802                 $k = str_repeat($key, ((256 / strlen($key)) + 1));
07803                 $rc4 = range(0, 255);
07804                 $j = 0;
07805                 for ($i = 0; $i < 256; ++$i) {
07806                     $t = $rc4[$i];
07807                     $j = ($j + $t + ord($k{$i})) % 256;
07808                     $rc4[$i] = $rc4[$j];
07809                     $rc4[$j] = $t;
07810                 }
07811                 $this->last_rc4_key = $key;
07812                 $this->last_rc4_key_c = $rc4;
07813             } else {
07814                 $rc4 = $this->last_rc4_key_c;
07815             }
07816             $len = strlen($text);
07817             $a = 0;
07818             $b = 0;
07819             $out = '';
07820             for ($i = 0; $i < $len; ++$i) {
07821                 $a = ($a + 1) % 256;
07822                 $t = $rc4[$a];
07823                 $b = ($b + $t) % 256;
07824                 $rc4[$a] = $rc4[$b];
07825                 $rc4[$b] = $t;
07826                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
07827                 $out .= chr(ord($text{$i}) ^ $k);
07828             }
07829             return $out;
07830         }
07831         
07840         protected function _md5_16($str) {
07841             return pack('H*', md5($str));
07842         }
07843         
07853         protected function _Ovalue($user_pass, $owner_pass) {
07854             $tmp = $this->_md5_16($owner_pass);
07855             $owner_RC4_key = substr($tmp, 0, 5);
07856             return $this->_RC4($owner_RC4_key, $user_pass);
07857         }
07858         
07866         protected function _Uvalue() {
07867             return $this->_RC4($this->encryption_key, $this->padding);
07868         }
07869         
07879         protected function _generateencryptionkey($user_pass, $owner_pass, $protection) {
07880             // Pad passwords
07881             $user_pass = substr($user_pass.$this->padding, 0, 32);
07882             $owner_pass = substr($owner_pass.$this->padding, 0, 32);
07883             // Compute O value
07884             $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
07885             // Compute encyption key
07886             $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
07887             $this->encryption_key = substr($tmp, 0, 5);
07888             // Compute U value
07889             $this->Uvalue = $this->_Uvalue();
07890             // Compute P value
07891             $this->Pvalue = -(($protection^255) + 1);
07892         }
07893         
07911         public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
07912             $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
07913             $protection = 192;
07914             foreach ($permissions as $permission) {
07915                 if (!isset($options[$permission])) {
07916                     $this->Error('Incorrect permission: '.$permission);
07917                 }
07918                 $protection += $options[$permission];
07919             }
07920             if ($owner_pass === null) {
07921                 $owner_pass = uniqid(rand());
07922             }
07923             $this->encrypted = true;
07924             $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
07925         }
07926         
07927         // END OF ENCRYPTION FUNCTIONS -------------------------
07928         
07929         // START TRANSFORMATIONS SECTION -----------------------
07930         
07939         public function StartTransform() {
07940             $this->_out('q');
07941             $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
07942             ++$this->transfmatrix_key;
07943             $this->transfmatrix[$this->transfmatrix_key] = array();
07944         }
07945         
07954         public function StopTransform() {
07955             $this->_out('Q');
07956             if (isset($this->transfmatrix[$this->transfmatrix_key])) {
07957                 array_pop($this->transfmatrix[$this->transfmatrix_key]);
07958                 --$this->transfmatrix_key;
07959             }
07960             array_pop($this->transfmrk[$this->page]);
07961         }
07971         public function ScaleX($s_x, $x='', $y='') {
07972             $this->Scale($s_x, 100, $x, $y);
07973         }
07974         
07984         public function ScaleY($s_y, $x='', $y='') {
07985             $this->Scale(100, $s_y, $x, $y);
07986         }
07987         
07997         public function ScaleXY($s, $x='', $y='') {
07998             $this->Scale($s, $s, $x, $y);
07999         }
08000         
08011         public function Scale($s_x, $s_y, $x='', $y='') {
08012             if ($x === '') {
08013                 $x = $this->x;
08014             }
08015             if ($y === '') {
08016                 $y = $this->y;
08017             }
08018             if ($this->rtl) {
08019                 $x = $this->w - $x;
08020             }
08021             if (($s_x == 0) OR ($s_y == 0)) {
08022                 $this->Error('Please do not use values equal to zero for scaling');
08023             }
08024             $y = ($this->h - $y) * $this->k;
08025             $x *= $this->k;
08026             //calculate elements of transformation matrix
08027             $s_x /= 100;
08028             $s_y /= 100;
08029             $tm[0] = $s_x;
08030             $tm[1] = 0;
08031             $tm[2] = 0;
08032             $tm[3] = $s_y;
08033             $tm[4] = $x * (1 - $s_x);
08034             $tm[5] = $y * (1 - $s_y);
08035             //scale the coordinate system
08036             $this->Transform($tm);
08037         }
08038         
08046         public function MirrorH($x='') {
08047             $this->Scale(-100, 100, $x);
08048         }
08049         
08057         public function MirrorV($y='') {
08058             $this->Scale(100, -100, '', $y);
08059         }
08060         
08069         public function MirrorP($x='',$y='') {
08070             $this->Scale(-100, -100, $x, $y);
08071         }
08072         
08082         public function MirrorL($angle=0, $x='',$y='') {
08083             $this->Scale(-100, 100, $x, $y);
08084             $this->Rotate(-2*($angle-90), $x, $y);
08085         }
08086         
08094         public function TranslateX($t_x) {
08095             $this->Translate($t_x, 0);
08096         }
08097         
08105         public function TranslateY($t_y) {
08106             $this->Translate(0, $t_y);
08107         }
08108         
08117         public function Translate($t_x, $t_y) {
08118             if ($this->rtl) {
08119                 $t_x = -$t_x;
08120             }
08121             //calculate elements of transformation matrix
08122             $tm[0] = 1;
08123             $tm[1] = 0;
08124             $tm[2] = 0;
08125             $tm[3] = 1;
08126             $tm[4] = $t_x * $this->k;
08127             $tm[5] = -$t_y * $this->k;
08128             //translate the coordinate system
08129             $this->Transform($tm);
08130         }
08131         
08141         public function Rotate($angle, $x='', $y='') {
08142             if ($x === '') {
08143                 $x = $this->x;
08144             }
08145             if ($y === '') {
08146                 $y = $this->y;
08147             }
08148             if ($this->rtl) {
08149                 $x = $this->w - $x;
08150                 $angle = -$angle;
08151             }
08152             $y = ($this->h - $y) * $this->k;
08153             $x *= $this->k;
08154             //calculate elements of transformation matrix
08155             $tm[0] = cos(deg2rad($angle));
08156             $tm[1] = sin(deg2rad($angle));
08157             $tm[2] = -$tm[1];
08158             $tm[3] = $tm[0];
08159             $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
08160             $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
08161             //rotate the coordinate system around ($x,$y)
08162             $this->Transform($tm);
08163         }
08164         
08174         public function SkewX($angle_x, $x='', $y='') {
08175             $this->Skew($angle_x, 0, $x, $y);
08176         }
08177         
08187         public function SkewY($angle_y, $x='', $y='') {
08188             $this->Skew(0, $angle_y, $x, $y);
08189         }
08190         
08201         public function Skew($angle_x, $angle_y, $x='', $y='') {
08202             if ($x === '') {
08203                 $x = $this->x;
08204             }
08205             if ($y === '') {
08206                 $y = $this->y;
08207             }
08208             if ($this->rtl) {
08209                 $x = $this->w - $x;
08210                 $angle_x = -$angle_x;
08211             }
08212             if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
08213                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
08214             }
08215             $x *= $this->k;
08216             $y = ($this->h - $y) * $this->k;
08217             //calculate elements of transformation matrix
08218             $tm[0] = 1;
08219             $tm[1] = tan(deg2rad($angle_y));
08220             $tm[2] = tan(deg2rad($angle_x));
08221             $tm[3] = 1;
08222             $tm[4] = -$tm[2] * $y;
08223             $tm[5] = -$tm[1] * $x;
08224             //skew the coordinate system
08225             $this->Transform($tm);
08226         }
08227         
08234         protected function Transform($tm) {
08235             $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
08236             // add tranformation matrix
08237             $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
08238             // update tranformation mark
08239             if (end($this->transfmrk[$this->page]) !== false) {
08240                 $key = key($this->transfmrk[$this->page]);
08241                 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
08242             }
08243         }
08244         
08245         // END TRANSFORMATIONS SECTION -------------------------
08246         
08247         
08248         // START GRAPHIC FUNCTIONS SECTION ---------------------
08249         // The following section is based on the code provided by David Hernandez Sanz
08250         
08258         public function SetLineWidth($width) {
08259             //Set line width
08260             $this->LineWidth = $width;
08261             $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
08262             if ($this->page > 0) {
08263                 $this->_out($this->linestyleWidth);
08264             }
08265         }
08266         
08274         public function GetLineWidth() {
08275             return $this->LineWidth;
08276         }
08277         
08299         public function SetLineStyle($style) {
08300             if (!is_array($style)) {
08301                 return;
08302             }
08303             extract($style);
08304             if (isset($width)) {
08305                 $width_prev = $this->LineWidth;
08306                 $this->SetLineWidth($width);
08307                 $this->LineWidth = $width_prev;
08308             }
08309             if (isset($cap)) {
08310                 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
08311                 if (isset($ca[$cap])) {
08312                     $this->linestyleCap = $ca[$cap].' J';
08313                     $this->_out($this->linestyleCap);
08314                 }
08315             }
08316             if (isset($join)) {
08317                 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
08318                 if (isset($ja[$join])) {
08319                     $this->linestyleJoin = $ja[$join].' j';
08320                     $this->_out($this->linestyleJoin);
08321                 }
08322             }
08323             if (isset($dash)) {
08324                 $dash_string = '';
08325                 if ($dash) {
08326                     if (preg_match('/^.+,/', $dash) > 0) {
08327                         $tab = explode(',', $dash);
08328                     } else {
08329                         $tab = array($dash);
08330                     }
08331                     $dash_string = '';
08332                     foreach ($tab as $i => $v) {
08333                         if ($i) {
08334                             $dash_string .= ' ';
08335                         }
08336                         $dash_string .= sprintf("%.2F", $v);
08337                     }
08338                 }
08339                 if (!isset($phase) OR !$dash) {
08340                     $phase = 0;
08341                 }
08342                 $this->linestyleDash = sprintf("[%s] %.2F d", $dash_string, $phase);
08343                 $this->_out($this->linestyleDash);
08344             }
08345             if (isset($color)) {
08346                 $this->SetDrawColorArray($color);
08347             }
08348         }
08349         
08350         /*
08351         * Set a draw point.
08352         * @param float $x Abscissa of point.
08353         * @param float $y Ordinate of point.
08354         * @access protected
08355         * @since 2.1.000 (2008-01-08)
08356         */
08357         protected function _outPoint($x, $y) {
08358             if ($this->rtl) {
08359                 $x = $this->w - $x;
08360             }
08361             $this->_out(sprintf("%.2F %.2F m", $x * $this->k, ($this->h - $y) * $this->k));
08362         }
08363         
08364         /*
08365         * Draws a line from last draw point.
08366         * @param float $x Abscissa of end point.
08367         * @param float $y Ordinate of end point.
08368         * @access protected
08369         * @since 2.1.000 (2008-01-08)
08370         */
08371         protected function _outLine($x, $y) {
08372             if ($this->rtl) {
08373                 $x = $this->w - $x;
08374             }
08375             $this->_out(sprintf("%.2F %.2F l", $x * $this->k, ($this->h - $y) * $this->k));
08376         }
08377         
08388         protected function _outRect($x, $y, $w, $h, $op) {
08389             if ($this->rtl) {
08390                 $x = $this->w - $x - $w;
08391             }
08392             $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
08393         }
08394         
08395         /*
08396         * Draws a Bezier curve from last draw point.
08397         * The Bezier curve is a tangent to the line between the control points at either end of the curve.
08398         * @param float $x1 Abscissa of control point 1.
08399         * @param float $y1 Ordinate of control point 1.
08400         * @param float $x2 Abscissa of control point 2.
08401         * @param float $y2 Ordinate of control point 2.
08402         * @param float $x3 Abscissa of end point.
08403         * @param float $y3 Ordinate of end point.
08404         * @access protected
08405         * @since 2.1.000 (2008-01-08)
08406         */
08407         protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
08408             if ($this->rtl) {
08409                 $x1 = $this->w - $x1;
08410                 $x2 = $this->w - $x2;
08411                 $x3 = $this->w - $x3;
08412             }
08413             $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));
08414         }
08415         
08427         public function Line($x1, $y1, $x2, $y2, $style=array()) {
08428             if (is_array($style)) {
08429                 $this->SetLineStyle($style);
08430             }
08431             $this->_outPoint($x1, $y1);
08432             $this->_outLine($x2, $y2);
08433             $this->_out(' S');
08434         }
08435         
08462         public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
08463             if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
08464                 $this->SetFillColorArray($fill_color);
08465             }
08466             switch ($style) {
08467                 case 'F': {
08468                     $op = 'f';
08469                     $border_style = array();
08470                     $this->_outRect($x, $y, $w, $h, $op);
08471                     break;
08472                 }
08473                 case 'DF':
08474                 case 'FD': {
08475                     if ((!$border_style) OR (isset($border_style['all']))) {
08476                         $op = 'B';
08477                         if (isset($border_style['all'])) {
08478                             $this->SetLineStyle($border_style['all']);
08479                             $border_style = array();
08480                         }
08481                     } else {
08482                         $op = 'f';
08483                     }
08484                     $this->_outRect($x, $y, $w, $h, $op);
08485                     break;
08486                 }
08487                 case 'CNZ': {
08488                     $op = 'W n';
08489                     $this->_outRect($x, $y, $w, $h, $op);
08490                     break;
08491                 }
08492                 case 'CEO': {
08493                     $op = 'W* n';
08494                     $this->_outRect($x, $y, $w, $h, $op);
08495                     break;
08496                 }
08497                 default: {
08498                     $op = 'S';
08499                     if ((!$border_style) OR (isset($border_style['all']))) {
08500                         if (isset($border_style['all']) AND $border_style['all']) {
08501                             $this->SetLineStyle($border_style['all']);
08502                             $border_style = array();
08503                         }
08504                         $this->_outRect($x, $y, $w, $h, $op);
08505                     }
08506                     break;
08507                 }
08508             }
08509             if ($border_style) {
08510                 $border_style2 = array();
08511                 foreach ($border_style as $line => $value) {
08512                     $lenght = strlen($line);
08513                     for ($i = 0; $i < $lenght; ++$i) {
08514                         $border_style2[$line[$i]] = $value;
08515                     }
08516                 }
08517                 $border_style = $border_style2;
08518                 if (isset($border_style['L']) AND $border_style['L']) {
08519                     $this->Line($x, $y, $x, $y + $h, $border_style['L']);
08520                 }
08521                 if (isset($border_style['T']) AND $border_style['T']) {
08522                     $this->Line($x, $y, $x + $w, $y, $border_style['T']);
08523                 }
08524                 if (isset($border_style['R']) AND $border_style['R']) {
08525                     $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
08526                 }
08527                 if (isset($border_style['B']) AND $border_style['B']) {
08528                     $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
08529                 }
08530             }
08531         }
08532         
08533         
08560         public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
08561             if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
08562                 $this->SetFillColorArray($fill_color);
08563             }
08564             switch ($style) {
08565                 case 'F': {
08566                     $op = 'f';
08567                     $line_style = array();
08568                     break;
08569                 }
08570                 case 'FD': 
08571                 case 'DF': {
08572                     $op = 'B';
08573                     break;
08574                 }
08575                 case 'CNZ': {
08576                     $op = 'W n';
08577                     break;
08578                 }
08579                 case 'CEO': {
08580                     $op = 'W* n';
08581                     break;
08582                 }
08583                 default: {
08584                     $op = 'S';
08585                     break;
08586                 }
08587             }
08588             if ($line_style) {
08589                 $this->SetLineStyle($line_style);
08590             }
08591             $this->_outPoint($x0, $y0);
08592             $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
08593             $this->_out($op);
08594         }
08595         
08617         public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
08618             if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
08619                 $this->SetFillColorArray($fill_color);
08620             }
08621             switch ($style) {
08622                 case 'F': {
08623                     $op = 'f';
08624                     $line_style = array();
08625                     break;
08626                 }
08627                 case 'FD':
08628                 case 'DF': {
08629                     $op = 'B';
08630                     break;
08631                 }
08632                 case 'CNZ': {
08633                     $op = 'W n';
08634                     break;
08635                 }
08636                 case 'CEO': {
08637                     $op = 'W* n';
08638                     break;
08639                 }
08640                 default: {
08641                     $op = 'S';
08642                     break;
08643                 }
08644             }
08645             if ($line_style) {
08646                 $this->SetLineStyle($line_style);
08647             }
08648             $this->_outPoint($x0, $y0);
08649             foreach ($segments as $segment) {
08650                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
08651                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
08652             }   
08653             $this->_out($op);
08654         }
08655         
08681         public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
08682             if ($angle) {
08683                 $this->StartTransform();
08684                 $this->Rotate($angle, $x0, $y0);
08685                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
08686                 $this->StopTransform();
08687                 return;
08688             }
08689             if ($rx) {
08690                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
08691                     $this->SetFillColorArray($fill_color);
08692                 }
08693                 switch ($style) {
08694                     case 'F': {
08695                         $op = 'f';
08696                         $line_style = array();
08697                         break;
08698                     }
08699                     case 'FD': 
08700                     case 'DF': {
08701                         $op = 'B';
08702                         break;
08703                     }
08704                     case 'C': {
08705                         $op = 's'; // Small 's' signifies closing the path as well
08706                         break;
08707                     }
08708                     case 'CNZ': {
08709                         $op = 'W n';
08710                         break;
08711                     }
08712                     case 'CEO': {
08713                         $op = 'W* n';
08714                         break;
08715                     }
08716                     default: {
08717                         $op = 'S';
08718                         break;
08719                     }
08720                 }
08721                 if ($line_style) {
08722                     $this->SetLineStyle($line_style);
08723                 }
08724                 if (!$ry) {
08725                     $ry = $rx;
08726                 }
08727                 $rx *= $this->k;
08728                 $ry *= $this->k;
08729                 if ($nc < 2) {
08730                     $nc = 2;
08731                 }
08732                 $astart = deg2rad((float) $astart);
08733                 $afinish = deg2rad((float) $afinish);
08734                 $total_angle = $afinish - $astart;
08735                 $dt = $total_angle / $nc;
08736                 $dtm = $dt / 3;
08737                 $x0 *= $this->k;
08738                 $y0 = ($this->h - $y0) * $this->k;
08739                 $t1 = $astart;
08740                 $a0 = $x0 + ($rx * cos($t1));
08741                 $b0 = $y0 + ($ry * sin($t1));
08742                 $c0 = -$rx * sin($t1);
08743                 $d0 = $ry * cos($t1);
08744                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
08745                 for ($i = 1; $i <= $nc; ++$i) {
08746                     // Draw this bit of the total curve
08747                     $t1 = ($i * $dt) + $astart;
08748                     $a1 = $x0 + ($rx * cos($t1));
08749                     $b1 = $y0 + ($ry * sin($t1));
08750                     $c1 = -$rx * sin($t1);
08751                     $d1 = $ry * cos($t1);
08752                     $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));
08753                     $a0 = $a1;
08754                     $b0 = $b1;
08755                     $c0 = $c1;
08756                     $d0 = $d1;
08757                 }
08758                 $this->_out($op);
08759             }
08760         }
08761         
08785         public function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
08786             $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
08787         }
08788 
08811         public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
08812             $this->Polygon($p, $style, $line_style, $fill_color, false);
08813         }
08814 
08837         public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
08838             $nc = count($p); // number of coordinates
08839             $np = $nc / 2; // number of points
08840             if ($closed) {
08841                 // close polygon by adding the first 2 points at the end (one line)
08842                 for ($i = 0; $i < 4; ++$i) {
08843                     $p[$nc + $i] = $p[$i];
08844                 }
08845                 // copy style for the last added line
08846                 if (isset($line_style[0])) {
08847                     $line_style[$np] = $line_style[0];
08848                 }           
08849                 $nc += 4;
08850             }
08851             if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
08852                 $this->SetFillColorArray($fill_color);
08853             }
08854             switch ($style) {
08855                 case 'F': {
08856                     $line_style = array();
08857                     $op = 'f';
08858                     break;
08859                 }
08860                 case 'FD': 
08861                 case 'DF': {
08862                     $op = 'B';
08863                     break;
08864                 }
08865                 case 'CNZ': {
08866                     $op = 'W n';
08867                     break;
08868                 }
08869                 case 'CEO': {
08870                     $op = 'W* n';
08871                     break;
08872                 }               
08873                 default: {
08874                     $op = 'S';
08875                     break;
08876                 }
08877             }
08878             $draw = true;
08879             if ($line_style) {
08880                 if (isset($line_style['all'])) {
08881                     $this->SetLineStyle($line_style['all']);
08882                 } else {
08883                     $draw = false;
08884                     if ($op == 'B') {
08885                         // draw fill
08886                         $op = 'f';
08887                         $this->_outPoint($p[0], $p[1]);
08888                         for ($i = 2; $i < $nc; $i = $i + 2) {
08889                             $this->_outLine($p[$i], $p[$i + 1]);
08890                         }
08891                         $this->_out($op);
08892                     }
08893                     // draw outline
08894                     $this->_outPoint($p[0], $p[1]);
08895                     for ($i = 2; $i < $nc; $i = $i + 2) {
08896                         $line_num = ($i / 2) - 1;
08897                         if (isset($line_style[$line_num])) {
08898                             if ($line_style[$line_num] != 0) {
08899                                 if (is_array($line_style[$line_num])) {
08900                                     $this->_out('S');
08901                                     $this->SetLineStyle($line_style[$line_num]);
08902                                     $this->_outPoint($p[$i - 2], $p[$i - 1]);
08903                                     $this->_outLine($p[$i], $p[$i + 1]);
08904                                     $this->_out('S');
08905                                     $this->_outPoint($p[$i], $p[$i + 1]);
08906                                 } else {
08907                                     $this->_outLine($p[$i], $p[$i + 1]);
08908                                 }
08909                             }
08910                         } else {
08911                             $this->_outLine($p[$i], $p[$i + 1]);
08912                         }
08913                     }
08914                     $this->_out($op);
08915                 }
08916             }
08917             if ($draw) {
08918                 $this->_outPoint($p[0], $p[1]);
08919                 for ($i = 2; $i < $nc; $i = $i + 2) {
08920                     $this->_outLine($p[$i], $p[$i + 1]);
08921                 }
08922                 $this->_out($op);
08923             }
08924         }
08925         
08962         public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
08963             if (3 > $ns) {
08964                 $ns = 3;
08965             }
08966             if ($draw_circle) {
08967                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
08968             }
08969             $p = array();
08970             for ($i = 0; $i < $ns; ++$i) {
08971                 $a = $angle + ($i * 360 / $ns);
08972                 $a_rad = deg2rad((float) $a);
08973                 $p[] = $x0 + ($r * sin($a_rad));
08974                 $p[] = $y0 + ($r * cos($a_rad));
08975             }
08976             $this->Polygon($p, $style, $line_style, $fill_color);
08977         }
08978         
09017         public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
09018             if ($nv < 2) {
09019                 $nv = 2;
09020             }
09021             if ($draw_circle) {
09022                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
09023             }
09024             $p2 = array();
09025             $visited = array();
09026             for ($i = 0; $i < $nv; ++$i) {
09027                 $a = $angle + ($i * 360 / $nv);
09028                 $a_rad = deg2rad((float) $a);
09029                 $p2[] = $x0 + ($r * sin($a_rad));
09030                 $p2[] = $y0 + ($r * cos($a_rad));
09031                 $visited[] = false;
09032             }
09033             $p = array();
09034             $i = 0;
09035             do {
09036                 $p[] = $p2[$i * 2];
09037                 $p[] = $p2[($i * 2) + 1];
09038                 $visited[$i] = true;
09039                 $i += $ng;
09040                 $i %= $nv;
09041             } while (!$visited[$i]);
09042             $this->Polygon($p, $style, $line_style, $fill_color);
09043         }
09044         
09066         public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
09067             if ('0000' == $round_corner) { // Not rounded
09068                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
09069             } else { // Rounded
09070                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
09071                     $this->SetFillColorArray($fill_color);
09072                 }
09073                 switch ($style) {
09074                     case 'F': {
09075                         $border_style = array();
09076                         $op = 'f';
09077                         break;
09078                     }
09079                     case 'FD': 
09080                     case 'DF': {
09081                         $op = 'B';
09082                         break;
09083                     }
09084                     case 'CNZ': {
09085                         $op = 'W n';
09086                         break;
09087                     }
09088                     case 'CEO': {
09089                         $op = 'W* n';
09090                         break;
09091                     }
09092                     default: {
09093                         $op = 'S';
09094                         break;
09095                     }
09096                 }
09097                 if ($border_style) {
09098                     $this->SetLineStyle($border_style);
09099                 }
09100                 $MyArc = 4 / 3 * (sqrt(2) - 1);
09101                 $this->_outPoint($x + $r, $y);
09102                 $xc = $x + $w - $r;
09103                 $yc = $y + $r;
09104                 $this->_outLine($xc, $y);
09105                 if ($round_corner[0]) {
09106                     $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
09107                 } else {
09108                     $this->_outLine($x + $w, $y);
09109                 }
09110                 $xc = $x + $w - $r;
09111                 $yc = $y + $h - $r;
09112                 $this->_outLine($x + $w, $yc);
09113                 if ($round_corner[1]) {
09114                     $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
09115                 } else {
09116                     $this->_outLine($x + $w, $y + $h);
09117                 }
09118                 $xc = $x + $r;
09119                 $yc = $y + $h - $r;
09120                 $this->_outLine($xc, $y + $h);
09121                 if ($round_corner[2]) {
09122                     $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
09123                 } else {
09124                     $this->_outLine($x, $y + $h);
09125                 }
09126                 $xc = $x + $r;
09127                 $yc = $y + $r;
09128                 $this->_outLine($x, $yc);
09129                 if ($round_corner[3]) {
09130                     $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
09131                 } else {
09132                     $this->_outLine($x, $y);
09133                     $this->_outLine($x + $r, $y);
09134                 }
09135                 $this->_out($op);
09136             }
09137         }
09138         
09151         public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
09152             // getting arrow direction angle
09153             // 0 deg angle is when both arms go along X axis. angle grows clockwise.
09154             $dir_angle = rad2deg(atan2(($y0 - $y1), ($x0 - $x1)));
09155             $sx1 = $x1;
09156             $sy1 = $y1;
09157             if ($head_style > 0) {
09158                 // calculate the stopping point for the arrow shaft
09159                 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos(deg2rad($dir_angle)));
09160                 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin(deg2rad($dir_angle)));
09161             } 
09162             // main arrow line / shaft
09163             $this->Line($x0, $y0, $sx1, $sy1);
09164             // left arrowhead arm tip
09165             $x2L = $x1 + ($arm_size * cos(deg2rad($dir_angle + $arm_angle)));
09166             $y2L = $y1 + ($arm_size * sin(deg2rad($dir_angle + $arm_angle)));
09167             // right arrowhead arm tip
09168             $x2R = $x1 + ($arm_size * cos(deg2rad($dir_angle - $arm_angle)));
09169             $y2R = $y1 + ($arm_size * sin(deg2rad($dir_angle - $arm_angle)));
09170             $mode = 'D';
09171             $style = array();
09172             switch ($head_style) {
09173                 case 0: {
09174                     // draw only arrowhead arms
09175                     $mode = 'D';
09176                     $style = array(1, 1, 0);
09177                     break;
09178                 }
09179                 case 1: {
09180                     // draw closed arrowhead, but no fill
09181                     $mode = 'D';
09182                     break;
09183                 }
09184                 case 2: {
09185                     // closed and filled arrowhead
09186                     $mode = 'DF';
09187                     break;
09188                 }
09189                 case 3: {
09190                     // filled arrowhead
09191                     $mode = 'F';
09192                     break;
09193                 }
09194             }
09195             $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
09196         }
09197         
09198         // END GRAPHIC FUNCTIONS SECTION -----------------------
09199         
09200         // BIDIRECTIONAL TEXT SECTION --------------------------
09210         protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
09211             return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $str, $forcertl), $setbom);
09212         }
09213         
09224         protected function utf8Bidi($ta, $str='', $forcertl=false) {
09225             global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
09226             // paragraph embedding level
09227             $pel = 0;
09228             // max level
09229             $maxlevel = 0;
09230             if ($this->empty_string($str)) {
09231                 // create string from array
09232                 $str = $this->UTF8ArrSubString($ta);
09233             }
09234             // check if string contains arabic text
09235             if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
09236                 $arabic = true;
09237             } else {
09238                 $arabic = false;
09239             }
09240             // check if string contains RTL text
09241             if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
09242                 return $ta;
09243             }
09244             
09245             // get number of chars
09246             $numchars = count($ta);
09247             
09248             if ($forcertl == 'R') {
09249                     $pel = 1;
09250             } elseif ($forcertl == 'L') {
09251                     $pel = 0;
09252             } else {
09253                 // P2. In each paragraph, find the first character of type L, AL, or R.
09254                 // 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.
09255                 for ($i=0; $i < $numchars; ++$i) {
09256                     $type = $unicode[$ta[$i]];
09257                     if ($type == 'L') {
09258                         $pel = 0;
09259                         break;
09260                     } elseif (($type == 'AL') OR ($type == 'R')) {
09261                         $pel = 1;
09262                         break;
09263                     }
09264                 }
09265             }
09266             
09267             // Current Embedding Level
09268             $cel = $pel;
09269             // directional override status
09270             $dos = 'N';
09271             $remember = array();
09272             // start-of-level-run
09273             $sor = $pel % 2 ? 'R' : 'L';
09274             $eor = $sor;
09275             
09276             // Array of characters data
09277             $chardata = Array();
09278             
09279             // 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.
09280             //  In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
09281             for ($i=0; $i < $numchars; ++$i) {
09282                 if ($ta[$i] == K_RLE) {
09283                     // X2. With each RLE, compute the least greater odd embedding level.
09284                     //  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.
09285                     //  b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
09286                     $next_level = $cel + ($cel % 2) + 1;
09287                     if ($next_level < 62) {
09288                         $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
09289                         $cel = $next_level;
09290                         $dos = 'N';
09291                         $sor = $eor;
09292                         $eor = $cel % 2 ? 'R' : 'L';
09293                     }
09294                 } elseif ($ta[$i] == K_LRE) {
09295                     // X3. With each LRE, compute the least greater even embedding level.
09296                     //  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.
09297                     //  b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
09298                     $next_level = $cel + 2 - ($cel % 2);
09299                     if ( $next_level < 62 ) {
09300                         $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
09301                         $cel = $next_level;
09302                         $dos = 'N';
09303                         $sor = $eor;
09304                         $eor = $cel % 2 ? 'R' : 'L';
09305                     }
09306                 } elseif ($ta[$i] == K_RLO) {
09307                     // X4. With each RLO, compute the least greater odd embedding level.
09308                     //  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.
09309                     //  b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
09310                     $next_level = $cel + ($cel % 2) + 1;
09311                     if ($next_level < 62) {
09312                         $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
09313                         $cel = $next_level;
09314                         $dos = 'R';
09315                         $sor = $eor;
09316                         $eor = $cel % 2 ? 'R' : 'L';
09317                     }
09318                 } elseif ($ta[$i] == K_LRO) {
09319                     // X5. With each LRO, compute the least greater even embedding level.
09320                     //  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.
09321                     //  b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
09322                     $next_level = $cel + 2 - ($cel % 2);
09323                     if ( $next_level < 62 ) {
09324                         $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
09325                         $cel = $next_level;
09326                         $dos = 'L';
09327                         $sor = $eor;
09328                         $eor = $cel % 2 ? 'R' : 'L';
09329                     }
09330                 } elseif ($ta[$i] == K_PDF) {
09331                     // 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.
09332                     if (count($remember)) {
09333                         $last = count($remember ) - 1;
09334                         if (($remember[$last]['num'] == K_RLE) OR 
09335                               ($remember[$last]['num'] == K_LRE) OR 
09336                               ($remember[$last]['num'] == K_RLO) OR 
09337                               ($remember[$last]['num'] == K_LRO)) {
09338                             $match = array_pop($remember);
09339                             $cel = $match['cel'];
09340                             $dos = $match['dos'];
09341                             $sor = $eor;
09342                             $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
09343                         }
09344                     }
09345                 } elseif (($ta[$i] != K_RLE) AND
09346                                  ($ta[$i] != K_LRE) AND
09347                                  ($ta[$i] != K_RLO) AND
09348                                  ($ta[$i] != K_LRO) AND
09349                                  ($ta[$i] != K_PDF)) {
09350                     // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
09351                     //  a. Set the level of the current character to the current embedding level.
09352                     //  b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
09353                     if ($dos != 'N') {
09354                         $chardir = $dos;
09355                     } else {
09356                         if (isset($unicode[$ta[$i]])) {
09357                             $chardir = $unicode[$ta[$i]];
09358                         } else {
09359                             $chardir = 'L';
09360                         }
09361                     }
09362                     // stores string characters and other information
09363                     $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
09364                 }
09365             } // end for each char
09366             
09367             // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
09368             // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
09369             // 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.
09370             
09371             // 3.3.3 Resolving Weak Types
09372             // 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.
09373             // Nonspacing marks are now resolved based on the previous characters.
09374             $numchars = count($chardata);
09375             
09376             // 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.
09377             $prevlevel = -1; // track level changes
09378             $levcount = 0; // counts consecutive chars at the same level
09379             for ($i=0; $i < $numchars; ++$i) {
09380                 if ($chardata[$i]['type'] == 'NSM') {
09381                     if ($levcount) {
09382                         $chardata[$i]['type'] = $chardata[$i]['sor'];
09383                     } elseif ($i > 0) {
09384                         $chardata[$i]['type'] = $chardata[($i-1)]['type'];
09385                     }
09386                 }
09387                 if ($chardata[$i]['level'] != $prevlevel) {
09388                     $levcount = 0;
09389                 } else {
09390                     ++$levcount;
09391                 }
09392                 $prevlevel = $chardata[$i]['level'];
09393             }
09394             
09395             // 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.
09396             $prevlevel = -1;
09397             $levcount = 0;
09398             for ($i=0; $i < $numchars; ++$i) {
09399                 if ($chardata[$i]['char'] == 'EN') {
09400                     for ($j=$levcount; $j >= 0; $j--) {
09401                         if ($chardata[$j]['type'] == 'AL') {
09402                             $chardata[$i]['type'] = 'AN';
09403                         } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
09404                             break;
09405                         }
09406                     }
09407                 }
09408                 if ($chardata[$i]['level'] != $prevlevel) {
09409                     $levcount = 0;
09410                 } else {
09411                     ++$levcount;
09412                 }
09413                 $prevlevel = $chardata[$i]['level'];
09414             }
09415             
09416             // W3. Change all ALs to R.
09417             for ($i=0; $i < $numchars; ++$i) {
09418                 if ($chardata[$i]['type'] == 'AL') {
09419                     $chardata[$i]['type'] = 'R';
09420                 } 
09421             }
09422             
09423             // 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.
09424             $prevlevel = -1;
09425             $levcount = 0;
09426             for ($i=0; $i < $numchars; ++$i) {
09427                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
09428                     if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
09429                         $chardata[$i]['type'] = 'EN';
09430                     } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
09431                         $chardata[$i]['type'] = 'EN';
09432                     } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
09433                         $chardata[$i]['type'] = 'AN';
09434                     }
09435                 }
09436                 if ($chardata[$i]['level'] != $prevlevel) {
09437                     $levcount = 0;
09438                 } else {
09439                     ++$levcount;
09440                 }
09441                 $prevlevel = $chardata[$i]['level'];
09442             }
09443             
09444             // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
09445             $prevlevel = -1;
09446             $levcount = 0;
09447             for ($i=0; $i < $numchars; ++$i) {
09448                 if ($chardata[$i]['type'] == 'ET') {
09449                     if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
09450                         $chardata[$i]['type'] = 'EN';
09451                     } else {
09452                         $j = $i+1;
09453                         while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
09454                             if ($chardata[$j]['type'] == 'EN') {
09455                                 $chardata[$i]['type'] = 'EN';
09456                                 break;
09457                             } elseif ($chardata[$j]['type'] != 'ET') {
09458                                 break;
09459                             }
09460                             ++$j;
09461                         }
09462                     }
09463                 }
09464                 if ($chardata[$i]['level'] != $prevlevel) {
09465                     $levcount = 0;
09466                 } else {
09467                     ++$levcount;
09468                 }
09469                 $prevlevel = $chardata[$i]['level'];
09470             }
09471             
09472             // W6. Otherwise, separators and terminators change to Other Neutral.
09473             $prevlevel = -1;
09474             $levcount = 0;
09475             for ($i=0; $i < $numchars; ++$i) {
09476                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
09477                     $chardata[$i]['type'] = 'ON';
09478                 }
09479                 if ($chardata[$i]['level'] != $prevlevel) {
09480                     $levcount = 0;
09481                 } else {
09482                     ++$levcount;
09483                 }
09484                 $prevlevel = $chardata[$i]['level'];
09485             }
09486             
09487             //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.
09488             $prevlevel = -1;
09489             $levcount = 0;
09490             for ($i=0; $i < $numchars; ++$i) {
09491                 if ($chardata[$i]['char'] == 'EN') {
09492                     for ($j=$levcount; $j >= 0; $j--) {
09493                         if ($chardata[$j]['type'] == 'L') {
09494                             $chardata[$i]['type'] = 'L';
09495                         } elseif ($chardata[$j]['type'] == 'R') {
09496                             break;
09497                         }
09498                     }
09499                 }
09500                 if ($chardata[$i]['level'] != $prevlevel) {
09501                     $levcount = 0;
09502                 } else {
09503                     ++$levcount;
09504                 }
09505                 $prevlevel = $chardata[$i]['level'];
09506             }
09507             
09508             // 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.
09509             $prevlevel = -1;
09510             $levcount = 0;
09511             for ($i=0; $i < $numchars; ++$i) {
09512                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
09513                     if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
09514                         $chardata[$i]['type'] = 'L';
09515                     } elseif (($chardata[$i]['type'] == 'N') AND
09516                      (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
09517                      (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
09518                         $chardata[$i]['type'] = 'R';
09519                     } elseif ($chardata[$i]['type'] == 'N') {
09520                         // N2. Any remaining neutrals take the embedding direction
09521                         $chardata[$i]['type'] = $chardata[$i]['sor'];
09522                     }
09523                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
09524                     // first char
09525                     if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
09526                         $chardata[$i]['type'] = 'L';
09527                     } elseif (($chardata[$i]['type'] == 'N') AND
09528                      (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
09529                      (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
09530                         $chardata[$i]['type'] = 'R';
09531                     } elseif ($chardata[$i]['type'] == 'N') {
09532                         // N2. Any remaining neutrals take the embedding direction
09533                         $chardata[$i]['type'] = $chardata[$i]['sor'];
09534                     }
09535                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
09536                     //last char
09537                     if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
09538                         $chardata[$i]['type'] = 'L';
09539                     } elseif (($chardata[$i]['type'] == 'N') AND
09540                      (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
09541                      (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
09542                         $chardata[$i]['type'] = 'R';
09543                     } elseif ($chardata[$i]['type'] == 'N') {
09544                         // N2. Any remaining neutrals take the embedding direction
09545                         $chardata[$i]['type'] = $chardata[$i]['sor'];
09546                     }
09547                 } elseif ($chardata[$i]['type'] == 'N') {
09548                     // N2. Any remaining neutrals take the embedding direction
09549                     $chardata[$i]['type'] = $chardata[$i]['sor'];
09550                 }
09551                 if ($chardata[$i]['level'] != $prevlevel) {
09552                     $levcount = 0;
09553                 } else {
09554                     ++$levcount;
09555                 }
09556                 $prevlevel = $chardata[$i]['level'];
09557             }
09558             
09559             // 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.
09560             // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
09561             for ($i=0; $i < $numchars; ++$i) {
09562                 $odd = $chardata[$i]['level'] % 2;
09563                 if ($odd) {
09564                     if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
09565                         $chardata[$i]['level'] += 1;
09566                     }
09567                 } else {
09568                     if ($chardata[$i]['type'] == 'R') {
09569                         $chardata[$i]['level'] += 1;
09570                     } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
09571                         $chardata[$i]['level'] += 2;
09572                     }
09573                 }
09574                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
09575             }
09576             
09577             // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
09578             //  1. Segment separators,
09579             //  2. Paragraph separators,
09580             //  3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
09581             //  4. Any sequence of white space characters at the end of the line.
09582             for ($i=0; $i < $numchars; ++$i) {
09583                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
09584                     $chardata[$i]['level'] = $pel;
09585                 } elseif ($chardata[$i]['type'] == 'WS') {
09586                     $j = $i+1;
09587                     while ($j < $numchars) {
09588                         if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
09589                             (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
09590                             $chardata[$i]['level'] = $pel;
09591                             break;
09592                         } elseif ($chardata[$j]['type'] != 'WS') {
09593                             break;
09594                         }
09595                         ++$j;
09596                     }
09597                 }
09598             }
09599             
09600             // Arabic Shaping
09601             // 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. 
09602             if ($arabic) {
09603                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
09604                 $alfletter = array(1570,1571,1573,1575);
09605                 $chardata2 = $chardata;
09606                 $laaletter = false;
09607                 $charAL = array();
09608                 $x = 0;
09609                 for ($i=0; $i < $numchars; ++$i) {
09610                     if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
09611                         $charAL[$x] = $chardata[$i];
09612                         $charAL[$x]['i'] = $i;
09613                         $chardata[$i]['x'] = $x;
09614                         ++$x;
09615                     }
09616                 }
09617                 $numAL = $x;
09618                 for ($i=0; $i < $numchars; ++$i) {
09619                     $thischar = $chardata[$i];
09620                     if ($i > 0) {
09621                         $prevchar = $chardata[($i-1)];
09622                     } else {
09623                         $prevchar = false;
09624                     }
09625                     if (($i+1) < $numchars) {
09626                         $nextchar = $chardata[($i+1)];
09627                     } else {
09628                         $nextchar = false;
09629                     }
09630                     if ($unicode[$thischar['char']] == 'AL') {
09631                         $x = $thischar['x'];
09632                         if ($x > 0) {
09633                             $prevchar = $charAL[($x-1)];
09634                         } else {
09635                             $prevchar = false;
09636                         }
09637                         if (($x+1) < $numAL) {
09638                             $nextchar = $charAL[($x+1)];
09639                         } else {
09640                             $nextchar = false;
09641                         }
09642                         // if laa letter
09643                         if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
09644                             $arabicarr = $laa_array;
09645                             $laaletter = true;
09646                             if ($x > 1) {
09647                                 $prevchar = $charAL[($x-2)];
09648                             } else {
09649                                 $prevchar = false;
09650                             }
09651                         } else {
09652                             $arabicarr = $unicode_arlet;
09653                             $laaletter = false;
09654                         }
09655                         if (($prevchar !== false) AND ($nextchar !== false) AND
09656                             (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
09657                             (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
09658                             ($prevchar['type'] == $thischar['type']) AND
09659                             ($nextchar['type'] == $thischar['type']) AND
09660                             ($nextchar['char'] != 1567)) {
09661                             if (in_array($prevchar['char'], $endedletter)) {
09662                                 if (isset($arabicarr[$thischar['char']][2])) {
09663                                     // initial
09664                                     $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
09665                                 }
09666                             } else {
09667                                 if (isset($arabicarr[$thischar['char']][3])) {
09668                                     // medial
09669                                     $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
09670                                 }
09671                             }
09672                         } elseif (($nextchar !== false) AND
09673                             (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
09674                             ($nextchar['type'] == $thischar['type']) AND
09675                             ($nextchar['char'] != 1567)) {
09676                             if (isset($arabicarr[$chardata[$i]['char']][2])) {
09677                                 // initial
09678                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
09679                             }
09680                         } elseif ((($prevchar !== false) AND
09681                             (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
09682                             ($prevchar['type'] == $thischar['type'])) OR
09683                             (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
09684                             // final
09685                             if (($i > 1) AND ($thischar['char'] == 1607) AND
09686                                 ($chardata[$i-1]['char'] == 1604) AND
09687                                 ($chardata[$i-2]['char'] == 1604)) {
09688                                 //Allah Word
09689                                 // mark characters to delete with false
09690                                 $chardata2[$i-2]['char'] = false;
09691                                 $chardata2[$i-1]['char'] = false; 
09692                                 $chardata2[$i]['char'] = 65010;
09693                             } else {
09694                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
09695                                     if (isset($arabicarr[$thischar['char']][0])) {
09696                                         // isolated
09697                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
09698                                     }
09699                                 } else {
09700                                     if (isset($arabicarr[$thischar['char']][1])) {
09701                                         // final
09702                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
09703                                     }
09704                                 }
09705                             }
09706                         } elseif (isset($arabicarr[$thischar['char']][0])) {
09707                             // isolated
09708                             $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
09709                         }
09710                         // if laa letter
09711                         if ($laaletter) {
09712                             // mark characters to delete with false
09713                             $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
09714                         }
09715                     } // end if AL (Arabic Letter)
09716                 } // end for each char
09717                 /* 
09718                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594. 
09719                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
09720                  */
09721                 $cw = &$this->CurrentFont['cw'];
09722                 for ($i = 0; $i < ($numchars-1); ++$i) {
09723                     if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
09724                         // check if the subtitution font is defined on current font
09725                         if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
09726                             $chardata2[$i]['char'] = false;
09727                             $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
09728                         }
09729                     }
09730                 }
09731                 // remove marked characters
09732                 foreach ($chardata2 as $key => $value) {
09733                     if ($value['char'] === false) {
09734                         unset($chardata2[$key]);
09735                     }
09736                 }
09737                 $chardata = array_values($chardata2);
09738                 $numchars = count($chardata);
09739                 unset($chardata2);
09740                 unset($arabicarr);
09741                 unset($laaletter);
09742                 unset($charAL);
09743             }
09744             
09745             // 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.
09746             for ($j=$maxlevel; $j > 0; $j--) {
09747                 $ordarray = Array();
09748                 $revarr = Array();
09749                 $onlevel = false;
09750                 for ($i=0; $i < $numchars; ++$i) {
09751                     if ($chardata[$i]['level'] >= $j) {
09752                         $onlevel = true;
09753                         if (isset($unicode_mirror[$chardata[$i]['char']])) {
09754                             // 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.
09755                             $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
09756                         }
09757                         $revarr[] = $chardata[$i];
09758                     } else {
09759                         if ($onlevel) {
09760                             $revarr = array_reverse($revarr);
09761                             $ordarray = array_merge($ordarray, $revarr);
09762                             $revarr = Array();
09763                             $onlevel = false;
09764                         }
09765                         $ordarray[] = $chardata[$i];
09766                     }
09767                 }
09768                 if ($onlevel) {
09769                     $revarr = array_reverse($revarr);
09770                     $ordarray = array_merge($ordarray, $revarr);
09771                 }
09772                 $chardata = $ordarray;
09773             }
09774             
09775             $ordarray = array();
09776             for ($i=0; $i < $numchars; ++$i) {
09777                 $ordarray[] = $chardata[$i]['char'];
09778             }
09779             
09780             return $ordarray;
09781         }
09782         
09783         // END OF BIDIRECTIONAL TEXT SECTION -------------------
09784         
09785         /*
09786         * Adds a bookmark.
09787         * @param string $txt bookmark description.
09788         * @param int $level bookmark level (minimum value is 0).
09789         * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
09790         * @param int $page target page number (leave empty for current page).
09791         * @access public
09792         * @author Olivier Plathey, Nicola Asuni
09793         * @since 2.1.002 (2008-02-12)
09794         */
09795         public function Bookmark($txt, $level=0, $y=-1, $page='') {
09796             if ($level < 0) {
09797                 $level = 0;
09798             }
09799             if (isset($this->outlines[0])) {
09800                 $lastoutline = end($this->outlines);
09801                 $maxlevel = $lastoutline['l'] + 1;
09802             } else {
09803                 $maxlevel = 0;
09804             }
09805             if ($level > $maxlevel) {
09806                 $level = $maxlevel;
09807             }
09808             if ($y == -1) {
09809                 $y = $this->GetY();
09810             }
09811             if (empty($page)) {
09812                 $page = $this->PageNo();
09813             }
09814             $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
09815         }
09816         
09817         /*
09818         * Create a bookmark PDF string.
09819         * @access protected
09820         * @author Olivier Plathey, Nicola Asuni
09821         * @since 2.1.002 (2008-02-12)
09822         */
09823         protected function _putbookmarks() {
09824             $nb = count($this->outlines);
09825             if ($nb == 0) {
09826                 return;
09827             }
09828             $lru = array();
09829             $level = 0;
09830             foreach ($this->outlines as $i => $o) {
09831                 if ($o['l'] > 0) {
09832                     $parent = $lru[($o['l'] - 1)];
09833                     //Set parent and last pointers
09834                     $this->outlines[$i]['parent'] = $parent;
09835                     $this->outlines[$parent]['last'] = $i;
09836                     if ($o['l'] > $level) {
09837                         //Level increasing: set first pointer
09838                         $this->outlines[$parent]['first'] = $i;
09839                     }
09840                 } else {
09841                     $this->outlines[$i]['parent'] = $nb;
09842                 }
09843                 if (($o['l'] <= $level) AND ($i > 0)) {
09844                     //Set prev and next pointers
09845                     $prev = $lru[$o['l']];
09846                     $this->outlines[$prev]['next'] = $i;
09847                     $this->outlines[$i]['prev'] = $prev;
09848                 }
09849                 $lru[$o['l']] = $i;
09850                 $level = $o['l'];
09851             }
09852             //Outline items
09853             $n = $this->n + 1;
09854             foreach ($this->outlines as $i => $o) {
09855                 $this->_newobj();
09856                 $this->_out('<</Title '.$this->_textstring($o['t']));
09857                 $this->_out('/Parent '.($n + $o['parent']).' 0 R');
09858                 if (isset($o['prev']))
09859                 $this->_out('/Prev '.($n + $o['prev']).' 0 R');
09860                 if (isset($o['next']))
09861                 $this->_out('/Next '.($n + $o['next']).' 0 R');
09862                 if (isset($o['first']))
09863                 $this->_out('/First '.($n + $o['first']).' 0 R');
09864                 if (isset($o['last']))
09865                 $this->_out('/Last '.($n + $o['last']).' 0 R');
09866                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))));
09867                 $this->_out('/Count 0>>');
09868                 $this->_out('endobj');
09869             }
09870             //Outline root
09871             $this->_newobj();
09872             $this->OutlineRoot = $this->n;
09873             $this->_out('<</Type /Outlines /First '.$n.' 0 R');
09874             $this->_out('/Last '.($n + $lru[0]).' 0 R>>');
09875             $this->_out('endobj');
09876         }
09877         
09878         
09879         // --- JAVASCRIPT ------------------------------------------------------
09880         
09881         /*
09882         * Adds a javascript
09883         * @param string $script Javascript code
09884         * @access public
09885         * @author Johannes G�ntert, Nicola Asuni
09886         * @since 2.1.002 (2008-02-12)
09887         */
09888         public function IncludeJS($script) {
09889             $this->javascript .= $script;
09890         }
09891 
09892         /*
09893         * Adds a javascript object and return object ID
09894         * @param string $script Javascript code
09895         * @param boolean $onload if true executes this object when opening the document
09896         * @return int internal object ID
09897         * @access public
09898         * @author Nicola Asuni
09899         * @since 4.8.000 (2009-09-07)
09900         */
09901         public function addJavascriptObject($script, $onload=false) {
09902             ++$this->js_obj_id;
09903             $this->js_objects[$this->js_obj_id] = array('js' => $script, 'onload' => $onload);
09904             return $this->js_obj_id;
09905         }
09906 
09907         /*
09908         * Create a javascript PDF string.
09909         * @access protected
09910         * @author Johannes G�ntert, Nicola Asuni
09911         * @since 2.1.002 (2008-02-12)
09912         */
09913         protected function _putjavascript() {
09914             if (empty($this->javascript) AND empty($this->js_objects)) {
09915                 return;
09916             }
09917             if (strpos($this->javascript, 'this.addField') > 0) {
09918                 if (!$this->ur) {
09919                     //$this->setUserRights();
09920                 }
09921                 // the following two lines are used to avoid form fields duplication after saving
09922                 // The addField method only works on Acrobat Writer, unless the document is signed with Adobe private key (UR3)
09923                 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
09924                 $jsb = "getField('tcpdfdocsaved').value='saved';";
09925                 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
09926             }
09927             $this->n_js = $this->_newobj();
09928             $this->_out('<<');
09929             $this->_out('/Names [');
09930             if (!empty($this->javascript)) {
09931                 $this->_out('(EmbeddedJS) '.($this->n + 1).' 0 R');
09932             }
09933             if (!empty($this->js_objects)) {
09934                 foreach ($this->js_objects as $key => $val) {
09935                     if ($val['onload']) {
09936                         $this->_out('(JS'.$key.') '.$key.' 0 R');
09937                     }
09938                 }
09939             }
09940             $this->_out(']');
09941             $this->_out('>>');
09942             $this->_out('endobj');
09943             // default Javascript object
09944             if (!empty($this->javascript)) {
09945                 $this->_newobj();
09946                 $this->_out('<<');
09947                 $this->_out('/S /JavaScript');
09948                 $this->_out('/JS '.$this->_textstring($this->javascript));
09949                 $this->_out('>>');
09950                 $this->_out('endobj');
09951             }
09952             // additional Javascript objects
09953             if (!empty($this->js_objects)) {
09954                 foreach ($this->js_objects as $key => $val) {
09955                     $this->offsets[$key] = $this->bufferlen;
09956                     $this->_out($key.' 0 obj');
09957                     $this->_out('<<');
09958                     $this->_out('/S /JavaScript');
09959                     $this->_out('/JS '.$this->_textstring($val['js']));
09960                     $this->_out('>>');
09961                     $this->_out('endobj');
09962                 }
09963             }           
09964         }
09965         
09966         /*
09967         * Convert color to javascript color.
09968         * @param string $color color name or #RRGGBB
09969         * @access protected
09970         * @author Denis Van Nuffelen, Nicola Asuni
09971         * @since 2.1.002 (2008-02-12)
09972         */
09973         protected function _JScolor($color) {
09974             static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
09975             if (substr($color,0,1) == '#') {
09976                 return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
09977             }
09978             if (!in_array($color,$aColors)) {
09979                 $this->Error('Invalid color: '.$color);
09980             }
09981             return 'color.'.$color;
09982         }
09983         
09984         /*
09985         * Adds a javascript form field.
09986         * @param string $type field type
09987         * @param string $name field name
09988         * @param int $x horizontal position
09989         * @param int $y vertical position
09990         * @param int $w width
09991         * @param int $h height
09992         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
09993         * @access protected
09994         * @author Denis Van Nuffelen, Nicola Asuni
09995         * @since 2.1.002 (2008-02-12)
09996         */
09997         protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
09998             if ($this->rtl) {
09999                 $x = $x - $w;
10000             }
10001             // the followind avoid fields duplication after saving the document
10002             $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
10003             $k = $this->k;
10004             $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
10005             $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
10006             while (list($key, $val) = each($prop)) {
10007                 if (strcmp(substr($key, -5), 'Color') == 0) {
10008                     $val = $this->_JScolor($val);
10009                 } else {
10010                     $val = "'".$val."'";
10011                 }
10012                 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
10013             }
10014             if ($this->rtl) {
10015                 $this->x -= $w;
10016             } else {
10017                 $this->x += $w;
10018             }
10019             $this->javascript .= '}';
10020         }
10021 
10022         // --- FORM FIELDS -----------------------------------------------------
10023 
10024         /*
10025         * Convert JavaScript form fields properties array to Annotation Properties array.
10026         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10027         * @return array of annotation properties
10028         * @access protected
10029         * @author Nicola Asuni
10030         * @since 4.8.000 (2009-09-06)
10031         */
10032         protected function getAnnotOptFromJSProp($prop) {
10033             if (isset($prop['aopt']) AND is_array($prop['aopt'])) {
10034                 // the annotation options area lready defined
10035                 return $prop['aopt'];
10036             }
10037             $opt = array(); // value to be returned
10038             // alignment: Controls how the text is laid out within the text field.
10039             if (isset($prop['alignment'])) {
10040                 switch ($prop['alignment']) {
10041                     case 'left': {
10042                         $opt['q'] = 0;
10043                         break;
10044                     }
10045                     case 'center': {
10046                         $opt['q'] = 1;
10047                         break;
10048                     }
10049                     case 'right': {
10050                         $opt['q'] = 2;
10051                         break;
10052                     }
10053                     default: {
10054                         $opt['q'] = ($this->rtl)?2:0;
10055                         break;
10056                     }
10057                 }
10058             }
10059             // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
10060             if (isset($prop['lineWidth'])) {
10061                 $linewidth = intval($prop['lineWidth']);
10062             } else {
10063                 $linewidth = 1;
10064             }
10065             // borderStyle: The border style for a field.
10066             if (isset($prop['borderStyle'])) {
10067                 switch ($prop['borderStyle']) {
10068                     case 'border.d':
10069                     case 'dashed': {
10070                         $opt['border'] = array(0, 0, $linewidth, array(3, 2));
10071                         $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2));
10072                         break;
10073                     }
10074                     case 'border.b':
10075                     case 'beveled': {
10076                         $opt['border'] = array(0, 0, $linewidth);
10077                         $opt['bs'] = array('w'=>$linewidth, 's'=>'B');
10078                         break;
10079                     }
10080                     case 'border.i':
10081                     case 'inset': {
10082                         $opt['border'] = array(0, 0, $linewidth);
10083                         $opt['bs'] = array('w'=>$linewidth, 's'=>'I');
10084                         break;
10085                     }
10086                     case 'border.u':
10087                     case 'underline': {
10088                         $opt['border'] = array(0, 0, $linewidth);
10089                         $opt['bs'] = array('w'=>$linewidth, 's'=>'U');
10090                         break;
10091                     }
10092                     default:
10093                     case 'border.s':
10094                     case 'solid': {
10095                         $opt['border'] = array(0, 0, $linewidth);
10096                         $opt['bs'] = array('w'=>$linewidth, 's'=>'S');
10097                         break;
10098                     }
10099                 }
10100             }
10101             if (isset($prop['border']) AND is_array($prop['border'])) {
10102                 $opt['border'] = $prop['border'];
10103             }
10104             if (!isset($opt['mk'])) {
10105                 $opt['mk'] = array();
10106             }
10107             if (!isset($opt['mk']['if'])) {
10108                 $opt['mk']['if'] = array();
10109             }
10110             $opt['mk']['if']['a'] = array(0.5, 0.5);
10111             // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
10112             if (isset($prop['buttonAlignX'])) {
10113                 $opt['mk']['if']['a'][0] = $prop['buttonAlignX'];
10114             }
10115             // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
10116             if (isset($prop['buttonAlignY'])) {
10117                 $opt['mk']['if']['a'][1] = $prop['buttonAlignY'];
10118             }
10119             // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
10120             if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) {
10121                 $opt['mk']['if']['fb'] = true;
10122             }           
10123             // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
10124             if (isset($prop['buttonScaleHow'])) {
10125                 switch ($prop['buttonScaleHow']) {
10126                     case 'scaleHow.proportional': {
10127                         $opt['mk']['if']['s'] = 'P';
10128                         break;
10129                     }
10130                     case 'scaleHow.anamorphic': {
10131                         $opt['mk']['if']['s'] = 'A';
10132                         break;
10133                     }
10134                 }
10135             }
10136             // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
10137             if (isset($prop['buttonScaleWhen'])) {
10138                 switch ($prop['buttonScaleWhen']) {
10139                     case 'scaleWhen.always': {
10140                         $opt['mk']['if']['sw'] = 'A';
10141                         break;
10142                     }
10143                     case 'scaleWhen.never': {
10144                         $opt['mk']['if']['sw'] = 'N';
10145                         break;
10146                     }
10147                     case 'scaleWhen.tooBig': {
10148                         $opt['mk']['if']['sw'] = 'B';
10149                         break;
10150                     }
10151                     case 'scaleWhen.tooSmall': {
10152                         $opt['mk']['if']['sw'] = 'S';
10153                         break;
10154                     }
10155                 }
10156             }
10157             // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
10158             if (isset($prop['buttonPosition'])) {
10159                 switch ($prop['buttonPosition']) {
10160                     case 0:
10161                     case 'position.textOnly': {
10162                         $opt['mk']['tp'] = 0;
10163                         break;
10164                     }
10165                     case 1:
10166                     case 'position.iconOnly': {
10167                         $opt['mk']['tp'] = 1;
10168                         break;
10169                     }
10170                     case 2:
10171                     case 'position.iconTextV': {
10172                         $opt['mk']['tp'] = 2;
10173                         break;
10174                     }
10175                     case 3:
10176                     case 'position.textIconV': {
10177                         $opt['mk']['tp'] = 3;
10178                         break;
10179                     }
10180                     case 4:
10181                     case 'position.iconTextH': {
10182                         $opt['mk']['tp'] = 4;
10183                         break;
10184                     }
10185                     case 5:
10186                     case 'position.textIconH': {
10187                         $opt['mk']['tp'] = 5;
10188                         break;
10189                     }
10190                     case 6:
10191                     case 'position.overlay': {
10192                         $opt['mk']['tp'] = 6;
10193                         break;
10194                     }
10195                 }               
10196             }
10197             // fillColor: Specifies the background color for a field.
10198             if (isset($prop['fillColor'])) {
10199                 if (is_array($prop['fillColor'])) {
10200                     $opt['mk']['bg'] = $prop['fillColor'];
10201                 } else {
10202                     $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']);
10203                 }
10204             }
10205             // strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width.
10206             if (isset($prop['strokeColor'])) {
10207                 if (is_array($prop['strokeColor'])) {
10208                     $opt['mk']['bc'] = $prop['strokeColor'];
10209                 } else {
10210                     $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']);
10211                 }
10212             }
10213             // rotation: The rotation of a widget in counterclockwise increments.
10214             if (isset($prop['rotation'])) {
10215                 $opt['mk']['r'] = $prop['rotation'];
10216             }
10217             // charLimit: Limits the number of characters that a user can type into a text field.
10218             if (isset($prop['charLimit'])) {
10219                 $opt['maxlen'] = intval($prop['charLimit']);
10220             }
10221             if (!isset($ff)) {
10222                 $ff = 0;
10223             }
10224             // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
10225             if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
10226                 $ff += 1 << 0;
10227             }
10228             // required: Specifies whether a field requires a value.
10229             if (isset($prop['required']) AND ($prop['required'] == 'true')) {
10230                 $ff += 1 << 1;
10231             }
10232             // multiline: Controls how text is wrapped within the field.
10233             if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) {
10234                 $ff += 1 << 12;
10235             }
10236             // password: Specifies whether the field should display asterisks when data is entered in the field.
10237             if (isset($prop['password']) AND ($prop['password'] == 'true')) {
10238                 $ff += 1 << 13;
10239             }
10240             // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
10241             if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) {
10242                 $ff += 1 << 14;
10243             }
10244             // Radio: If set, the field is a set of radio buttons.
10245             if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) {
10246                 $ff += 1 << 15;
10247             }
10248             // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
10249             if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) {
10250                 $ff += 1 << 16;
10251             }
10252             // Combo: If set, the field is a combo box; if clear, the field is a list box.
10253             if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) {
10254                 $ff += 1 << 17;
10255             }
10256             // editable: Controls whether a combo box is editable.
10257             if (isset($prop['editable']) AND ($prop['editable'] == 'true')) {
10258                 $ff += 1 << 18;
10259             }
10260             // Sort: If set, the field's option items shall be sorted alphabetically.
10261             if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) {
10262                 $ff += 1 << 19;
10263             }
10264             // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
10265             if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) {
10266                 $ff += 1 << 20;
10267             }
10268             // multipleSelection: If true, indicates that a list box allows a multiple selection of items.
10269             if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) {
10270                 $ff += 1 << 21;
10271             }
10272             // doNotSpellCheck: If true, spell checking is not performed on this editable text field.
10273             if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) {
10274                 $ff += 1 << 22;
10275             }
10276             // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
10277             if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) {
10278                 $ff += 1 << 23;
10279             }
10280             // comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set.
10281             if (isset($prop['comb']) AND ($prop['comb'] == 'true')) {
10282                 $ff += 1 << 24;
10283             }
10284             // radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons.
10285             if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) {
10286                 $ff += 1 << 25;
10287             }
10288             // richText: If true, the field allows rich text formatting.
10289             if (isset($prop['richText']) AND ($prop['richText'] == 'true')) {
10290                 $ff += 1 << 25;
10291             }
10292             // commitOnSelChange: Controls whether a field value is committed after a selection change.
10293             if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) {
10294                 $ff += 1 << 26;
10295             }
10296             $opt['ff'] = $ff;
10297             // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
10298             if (isset($prop['defaultValue'])) {
10299                 $opt['dv'] = $prop['defaultValue'];
10300             }
10301             $f = 4; // default value for annotation flags
10302             // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
10303             if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
10304                 $f += 1 << 6;
10305             }
10306             // display: Controls whether the field is hidden or visible on screen and in print.
10307             if (isset($prop['display'])) {
10308                 if ($prop['display'] == 'display.visible') {
10309                     //
10310                 } elseif ($prop['display'] == 'display.hidden') {
10311                     $f += 1 << 1;
10312                 } elseif ($prop['display'] == 'display.noPrint') {
10313                     $f -= 1 << 2;
10314                 } elseif ($prop['display'] == 'display.noView') {
10315                     $f += 1 << 5;
10316                 }
10317             }
10318             $opt['f'] = $f;
10319             // currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
10320             if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) {
10321                 $opt['i'] = $prop['currentValueIndices'];
10322             }
10323             // value: The value of the field data that the user has entered.
10324             if (isset($prop['value'])) {
10325                 if (is_array($prop['value'])) {
10326                     $opt['opt'] = array();
10327                     foreach ($prop['value'] AS $key => $optval) {
10328                         // exportValues: An array of strings representing the export values for the field.
10329                         if (isset($prop['exportValues'][$key])) {
10330                             $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]);
10331                         } else {
10332                             $opt['opt'][$key] = $prop['value'][$key];
10333                         }
10334                     }
10335                 } else {
10336                     $opt['v'] = $prop['value'];
10337                 }
10338             }
10339             // richValue: This property specifies the text contents and formatting of a rich text field.
10340             if (isset($prop['richValue'])) {
10341                 $opt['rv'] = $prop['richValue'];
10342             }
10343             // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
10344             if (isset($prop['submitName'])) {
10345                 $opt['tm'] = $prop['submitName'];
10346             }
10347             // name: Fully qualified field name.
10348             if (isset($prop['name'])) {
10349                 $opt['t'] = $prop['name'];
10350             }
10351             // userName: The user name (short description string) of the field.
10352             if (isset($prop['userName'])) {
10353                 $opt['tu'] = $prop['userName'];
10354             }
10355             // highlight: Defines how a button reacts when a user clicks it.
10356             if (isset($prop['highlight'])) {
10357                 switch ($prop['highlight']) {
10358                     case 'none':
10359                     case 'highlight.n': {
10360                         $opt['h'] = 'N';
10361                         break;
10362                     }
10363                     case 'invert':
10364                     case 'highlight.i': {
10365                         $opt['h'] = 'i';
10366                         break;
10367                     }
10368                     case 'push':
10369                     case 'highlight.p': {
10370                         $opt['h'] = 'P';
10371                         break;
10372                     }
10373                     case 'outline':
10374                     case 'highlight.o': {
10375                         $opt['h'] = 'O';
10376                         break;
10377                     }
10378                 }               
10379             }
10380             // Unsupported options:
10381             // - calcOrderIndex: Changes the calculation order of fields in the document.
10382             // - delay: Delays the redrawing of a field's appearance.
10383             // - defaultStyle: This property defines the default style attributes for the form field.
10384             // - style: Allows the user to set the glyph style of a check box or radio button.
10385             // - textColor, textFont, textSize
10386             return $opt;
10387         }
10388         
10389         /*
10390         * Set default properties for form fields.
10391         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10392         * @access public
10393         * @author Nicola Asuni
10394         * @since 4.8.000 (2009-09-06)
10395         */
10396         public function setFormDefaultProp($prop=array()) {
10397             $this->default_form_prop = $prop;
10398         }
10399         
10400         /*
10401         * Return the default properties for form fields.
10402         * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10403         * @access public
10404         * @author Nicola Asuni
10405         * @since 4.8.000 (2009-09-06)
10406         */
10407         public function getFormDefaultProp() {
10408             return $this->default_form_prop;
10409         }
10410         
10411         /*
10412         * Creates a text field
10413         * @param string $name field name
10414         * @param float $w Width of the rectangle
10415         * @param float $h Height of the rectangle
10416         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10417         * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
10418         * @param float $x Abscissa of the upper-left corner of the rectangle
10419         * @param float $y Ordinate of the upper-left corner of the rectangle
10420         * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10421         * @access public
10422         * @author Nicola Asuni
10423         * @since 4.8.000 (2009-09-07)
10424         */
10425         public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
10426             if ($x === '') {
10427                 $x = $this->x;
10428             }
10429             if ($y === '') {
10430                 $y = $this->y;
10431             }
10432             if ($js) {
10433                 $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
10434                 return;
10435             }
10436             // get default style
10437             $prop = array_merge($this->getFormDefaultProp(), $prop);
10438             // get annotation data
10439             $popt = $this->getAnnotOptFromJSProp($prop);
10440             // set default appearance stream
10441             $font = $this->FontFamily;
10442             $fontkey = array_search($font, $this->fontkeys);
10443             if (!in_array($fontkey, $this->annotation_fonts)) {
10444                 $this->annotation_fonts[$font] = $fontkey;
10445             }
10446             $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
10447             $popt['da'] = $fontstyle;
10448             $popt['ap'] = array();
10449             $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
10450             // merge options
10451             $opt = array_merge($popt, $opt);
10452             // remove some conflicting options
10453             unset($opt['bs']);
10454             // set remaining annotation data
10455             $opt['Subtype'] = 'Widget';
10456             $opt['ft'] = 'Tx';
10457             $opt['t'] = $name;
10458             /*
10459             Additional annotation's parameters (check _putannotsobj() method):
10460             //$opt['f']
10461             //$opt['ap']
10462             //$opt['as']
10463             //$opt['bs']
10464             //$opt['be']
10465             //$opt['c']
10466             //$opt['border']
10467             //$opt['h']
10468             //$opt['mk']
10469             //$opt['mk']['r']
10470             //$opt['mk']['bc']
10471             //$opt['mk']['bg']
10472             //$opt['mk']['ca']
10473             //$opt['mk']['rc']
10474             //$opt['mk']['ac']
10475             //$opt['mk']['i']
10476             //$opt['mk']['ri']
10477             //$opt['mk']['ix']
10478             //$opt['mk']['if']
10479             //$opt['mk']['if']['sw']
10480             //$opt['mk']['if']['s']
10481             //$opt['mk']['if']['a']
10482             //$opt['mk']['if']['fb']
10483             //$opt['mk']['tp']
10484             //$opt['tu']
10485             //$opt['tm']
10486             //$opt['ff']
10487             //$opt['v']
10488             //$opt['dv']
10489             //$opt['a']
10490             //$opt['aa']
10491             //$opt['q']
10492             */
10493             $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
10494             if ($this->rtl) {
10495                 $this->x -= $w;
10496             } else {
10497                 $this->x += $w;
10498             }
10499         }
10500 
10501         /*
10502         * Creates a RadioButton field
10503         * @param string $name field name
10504         * @param int $w width
10505         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10506         * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
10507         * @param string $onvalue value to be returned if selected.
10508         * @param boolean $checked define the initial state.
10509         * @param float $x Abscissa of the upper-left corner of the rectangle
10510         * @param float $y Ordinate of the upper-left corner of the rectangle
10511         * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10512         * @access public
10513         * @author Nicola Asuni
10514         * @since 4.8.000 (2009-09-07)
10515         */
10516         public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
10517             if ($x === '') {
10518                 $x = $this->x;
10519             }
10520             if ($y === '') {
10521                 $y = $this->y;
10522             }
10523             if ($js) {
10524                 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
10525                 return;
10526             }
10527             if ($this->empty_string($onvalue)) {
10528                 $onvalue = 'On';
10529             }
10530             if ($checked) {
10531                 $defval = $onvalue;
10532             } else {
10533                 $defval = 'Off';
10534             }
10535             // set data for parent group
10536             if (!isset($this->radiobutton_groups[$this->page])) {
10537                 $this->radiobutton_groups[$this->page] = array();
10538             }
10539             if (!isset($this->radiobutton_groups[$this->page][$name])) {
10540                 $this->radiobutton_groups[$this->page][$name] = array();
10541                 ++$this->annot_obj_id;
10542                 $this->radio_groups[] = $this->annot_obj_id;
10543             }
10544             // save object ID to be added on Kids entry on parent object
10545             $this->radiobutton_groups[$this->page][$name][] = array('kid' => ($this->annot_obj_id + 1), 'def' => $defval);
10546             // get default style
10547             $prop = array_merge($this->getFormDefaultProp(), $prop);
10548             $prop['NoToggleToOff'] = 'true';
10549             $prop['Radio'] = 'true';
10550             $prop['borderStyle'] = 'inset';
10551             // get annotation data
10552             $popt = $this->getAnnotOptFromJSProp($prop);
10553             // set additional default values
10554             $font = 'zapfdingbats';
10555             $this->AddFont($font);
10556             $fontkey = array_search($font, $this->fontkeys);
10557             if (!in_array($fontkey, $this->annotation_fonts)) {
10558                 $this->annotation_fonts[$font] = $fontkey;
10559             }
10560             $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
10561             $popt['da'] = $fontstyle;
10562             $popt['ap'] = array();
10563             $popt['ap']['n'] = array();
10564             $popt['ap']['n'][$onvalue] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
10565             $popt['ap']['n']['Off'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
10566             if (!isset($popt['mk'])) {
10567                 $popt['mk'] = array();
10568             }
10569             $popt['mk']['ca'] = '(l)';
10570             // merge options
10571             $opt = array_merge($popt, $opt);
10572             // set remaining annotation data
10573             $opt['Subtype'] = 'Widget';
10574             $opt['ft'] = 'Btn';
10575             if ($checked) {
10576                 $opt['v'] = array('/'.$onvalue);
10577                 $opt['as'] = $onvalue;
10578             } else {
10579                 $opt['as'] = 'Off';
10580             }
10581             $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
10582             if ($this->rtl) {
10583                 $this->x -= $w;
10584             } else {
10585                 $this->x += $w;
10586             }
10587         }
10588         
10589         /*
10590         * Creates a List-box field
10591         * @param string $name field name
10592         * @param int $w width
10593         * @param int $h height
10594         * @param array $values array containing the list of values.
10595         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10596         * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
10597         * @param float $x Abscissa of the upper-left corner of the rectangle
10598         * @param float $y Ordinate of the upper-left corner of the rectangle
10599         * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10600         * @access public
10601         * @author Nicola Asuni
10602         * @since 4.8.000 (2009-09-07)
10603         */
10604         public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
10605             if ($x === '') {
10606                 $x = $this->x;
10607             }
10608             if ($y === '') {
10609                 $y = $this->y;
10610             }
10611             if ($js) {
10612                 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
10613                 $s = '';
10614                 foreach ($values as $value) {
10615                     $s .= "'".addslashes($value)."',";
10616                 }
10617                 $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
10618                 return;
10619             }
10620             // get default style
10621             $prop = array_merge($this->getFormDefaultProp(), $prop);
10622             // get annotation data
10623             $popt = $this->getAnnotOptFromJSProp($prop);
10624             // set additional default values
10625             $font = $this->FontFamily;
10626             $fontkey = array_search($font, $this->fontkeys);
10627             if (!in_array($fontkey, $this->annotation_fonts)) {
10628                 $this->annotation_fonts[$font] = $fontkey;
10629             }
10630             $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
10631             $popt['da'] = $fontstyle;
10632             $popt['ap'] = array();
10633             $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
10634             // merge options
10635             $opt = array_merge($popt, $opt);
10636             // set remaining annotation data
10637             $opt['Subtype'] = 'Widget';
10638             $opt['ft'] = 'Ch';
10639             $opt['t'] = $name;
10640             $opt['opt'] = $values;
10641             $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
10642             if ($this->rtl) {
10643                 $this->x -= $w;
10644             } else {
10645                 $this->x += $w;
10646             }
10647         }
10648         
10649         /*
10650         * Creates a Combo-box field
10651         * @param string $name field name
10652         * @param int $w width
10653         * @param int $h height
10654         * @param array $values array containing the list of values.
10655         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10656         * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
10657         * @param float $x Abscissa of the upper-left corner of the rectangle
10658         * @param float $y Ordinate of the upper-left corner of the rectangle
10659         * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10660         * @access public
10661         * @author Nicola Asuni
10662         * @since 4.8.000 (2009-09-07)
10663         */
10664         public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
10665             if ($x === '') {
10666                 $x = $this->x;
10667             }
10668             if ($y === '') {
10669                 $y = $this->y;
10670             }
10671             if ($js) {
10672                 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
10673                 $s = '';
10674                 foreach ($values as $value) {
10675                     $s .= "'".addslashes($value)."',";
10676                 }
10677                 $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
10678                 return;
10679             }
10680             // get default style
10681             $prop = array_merge($this->getFormDefaultProp(), $prop);
10682             $prop['Combo'] = true;
10683             // get annotation data
10684             $popt = $this->getAnnotOptFromJSProp($prop);
10685             // set additional default options
10686             $font = $this->FontFamily;
10687             $fontkey = array_search($font, $this->fontkeys);
10688             if (!in_array($fontkey, $this->annotation_fonts)) {
10689                 $this->annotation_fonts[$font] = $fontkey;
10690             }
10691             $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
10692             $popt['da'] = $fontstyle;
10693             $popt['ap'] = array();
10694             $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
10695             // merge options
10696             $opt = array_merge($popt, $opt);
10697             // set remaining annotation data
10698             $opt['Subtype'] = 'Widget';
10699             $opt['ft'] = 'Ch';
10700             $opt['t'] = $name;
10701             $opt['opt'] = $values;
10702             $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
10703             if ($this->rtl) {
10704                 $this->x -= $w;
10705             } else {
10706                 $this->x += $w;
10707             }
10708         }
10709         
10710         /*
10711         * Creates a CheckBox field
10712         * @param string $name field name
10713         * @param int $w width
10714         * @param boolean $checked define the initial state.
10715         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10716         * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
10717         * @param string $onvalue value to be returned if selected.
10718         * @param float $x Abscissa of the upper-left corner of the rectangle
10719         * @param float $y Ordinate of the upper-left corner of the rectangle
10720         * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10721         * @access public
10722         * @author Nicola Asuni
10723         * @since 4.8.000 (2009-09-07)
10724         */
10725         public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
10726             if ($x === '') {
10727                 $x = $this->x;
10728             }
10729             if ($y === '') {
10730                 $y = $this->y;
10731             }
10732             if ($js) {
10733                 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
10734                 return;
10735             }
10736             if (!isset($prop['value'])) {
10737                 $prop['value'] = array('Yes');
10738             }
10739             // get default style
10740             $prop = array_merge($this->getFormDefaultProp(), $prop);
10741             $prop['borderStyle'] = 'inset';
10742             // get annotation data
10743             $popt = $this->getAnnotOptFromJSProp($prop);
10744             // set additional default options
10745             $font = 'zapfdingbats';
10746             $this->AddFont($font);
10747             $fontkey = array_search($font, $this->fontkeys);
10748             if (!in_array($fontkey, $this->annotation_fonts)) {
10749                 $this->annotation_fonts[$font] = $fontkey;
10750             }
10751             $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
10752             $popt['da'] = $fontstyle;
10753             $popt['ap'] = array();
10754             $popt['ap']['n'] = array();
10755             $popt['ap']['n']['Yes'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
10756             $popt['ap']['n']['Off'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
10757             // merge options
10758             $opt = array_merge($popt, $opt);
10759             // set remaining annotation data
10760             $opt['Subtype'] = 'Widget';
10761             $opt['ft'] = 'Btn';
10762             $opt['t'] = $name;
10763             $opt['opt'] = array($onvalue);
10764             if ($checked) {
10765                 $opt['v'] = array('/0');
10766                 $opt['as'] = 'Yes';
10767             } else {
10768                 $opt['v'] = array('/Off');
10769                 $opt['as'] = 'Off';
10770             }
10771             $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
10772             if ($this->rtl) {
10773                 $this->x -= $w;
10774             } else {
10775                 $this->x += $w;
10776             }
10777         }
10778         
10779         /*
10780         * Creates a button field
10781         * @param string $name field name
10782         * @param int $w width
10783         * @param int $h height
10784         * @param string $caption caption.
10785         * @param mixed $action action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008.
10786         * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10787         * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
10788         * @param float $x Abscissa of the upper-left corner of the rectangle
10789         * @param float $y Ordinate of the upper-left corner of the rectangle
10790         * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10791         * @access public
10792         * @author Nicola Asuni
10793         * @since 4.8.000 (2009-09-07)
10794         */
10795         public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
10796             if ($x === '') {
10797                 $x = $this->x;
10798             }
10799             if ($y === '') {
10800                 $y = $this->y;
10801             }
10802             if ($js) {
10803                 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
10804                 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
10805                 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
10806                 $this->javascript .= 'f'.$name.".highlight='push';\n";
10807                 $this->javascript .= 'f'.$name.".print=false;\n";
10808                 return;
10809             }
10810             // get default style
10811             $prop = array_merge($this->getFormDefaultProp(), $prop);
10812             $prop['Pushbutton'] = 'true';
10813             $prop['highlight'] = 'push';
10814             $prop['display'] = 'display.noPrint';
10815             // get annotation data
10816             $popt = $this->getAnnotOptFromJSProp($prop);
10817             // set additional default options
10818             if (!isset($popt['mk'])) {
10819                 $popt['mk'] = array();
10820             }
10821             $popt['mk']['ca'] = $this->_textstring($caption);
10822             $popt['mk']['rc'] = $this->_textstring($caption);
10823             $popt['mk']['ac'] = $this->_textstring($caption);
10824             $font = $this->FontFamily;
10825             $fontkey = array_search($font, $this->fontkeys);
10826             if (!in_array($fontkey, $this->annotation_fonts)) {
10827                 $this->annotation_fonts[$font] = $fontkey;
10828             }
10829             $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
10830             $popt['da'] = $fontstyle;
10831             $popt['ap'] = array();
10832             $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
10833             // merge options
10834             $opt = array_merge($popt, $opt);
10835             // set remaining annotation data
10836             $opt['Subtype'] = 'Widget';
10837             $opt['ft'] = 'Btn';
10838             $opt['t'] = $caption;
10839             $opt['v'] = $name;
10840             if (!empty($action)) {
10841                 if (is_array($action)) {
10842                     // form action options as on section 12.7.5 of PDF32000_2008.
10843                     $opt['aa'] = '/D <<';
10844                     $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
10845                     foreach ($action AS $key => $val) {
10846                         if (($key == 'S') AND in_array($val, $bmode)) {
10847                             $opt['aa'] .= ' /S /'.$val;
10848                         } elseif (($key == 'F') AND (!empty($val))) {
10849                             $opt['aa'] .= ' /F '.$this->_datastring($val);
10850                         } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
10851                             $opt['aa'] .= ' /Fields [';
10852                             foreach ($val AS $field) {
10853                                 $opt['aa'] .= ' '.$this->_textstring($field);
10854                             }
10855                             $opt['aa'] .= ']';
10856                         } elseif (($key == 'Flags')) {
10857                             $ff = 0;
10858                             if (is_array($val)) {
10859                                 foreach ($val AS $flag) {
10860                                     switch ($flag) {
10861                                         case 'Include/Exclude': {
10862                                             $ff += 1 << 0;
10863                                             break;
10864                                         }
10865                                         case 'IncludeNoValueFields': {
10866                                             $ff += 1 << 1;
10867                                             break;
10868                                         }
10869                                         case 'ExportFormat': {
10870                                             $ff += 1 << 2;
10871                                             break;
10872                                         }
10873                                         case 'GetMethod': {
10874                                             $ff += 1 << 3;
10875                                             break;
10876                                         }
10877                                         case 'SubmitCoordinates': {
10878                                             $ff += 1 << 4;
10879                                             break;
10880                                         }
10881                                         case 'XFDF': {
10882                                             $ff += 1 << 5;
10883                                             break;
10884                                         }
10885                                         case 'IncludeAppendSaves': {
10886                                             $ff += 1 << 6;
10887                                             break;
10888                                         }
10889                                         case 'IncludeAnnotations': {
10890                                             $ff += 1 << 7;
10891                                             break;
10892                                         }
10893                                         case 'SubmitPDF': {
10894                                             $ff += 1 << 8;
10895                                             break;
10896                                         }
10897                                         case 'CanonicalFormat': {
10898                                             $ff += 1 << 9;
10899                                             break;
10900                                         }
10901                                         case 'ExclNonUserAnnots': {
10902                                             $ff += 1 << 10;
10903                                             break;
10904                                         }
10905                                         case 'ExclFKey': {
10906                                             $ff += 1 << 11;
10907                                             break;
10908                                         }
10909                                         case 'EmbedForm': {
10910                                             $ff += 1 << 13;
10911                                             break;
10912                                         }
10913                                     }
10914                                 }
10915                             } else {
10916                                 $ff = intval($val);
10917                             }
10918                             $opt['aa'] .= ' /Flags '.$ff;
10919                         }
10920                     }
10921                     $opt['aa'] .= ' >>';
10922                 } else {
10923                     // Javascript action or raw action command
10924                     $js_obj_id = $this->addJavascriptObject($action);
10925                     $opt['aa'] = '/D '.$js_obj_id.' 0 R';
10926                 }
10927             }
10928             $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
10929             if ($this->rtl) {
10930                 $this->x -= $w;
10931             } else {
10932                 $this->x += $w;
10933             }
10934         }
10935         
10936         // --- END FORMS FIELDS ------------------------------------------------
10937         
10938         /*
10939         * Add certification signature (DocMDP or UR3)
10940         * You can set only one signature type
10941         * @access protected
10942         * @author Nicola Asuni
10943         * @since 4.6.008 (2009-05-07)
10944         */
10945         protected function _putsignature() {
10946             if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
10947                 return;
10948             }
10949             $this->_out('/Type /Sig');
10950             $this->_out('/Filter /Adobe.PPKLite');
10951             $this->_out('/SubFilter /adbe.pkcs7.detached');
10952             $this->_out($this->byterange_string);
10953             $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
10954             $this->_out('/Reference');
10955             $this->_out('[');
10956             $this->_out('<<');
10957             $this->_out('/Type /SigRef');
10958             if ($this->signature_data['cert_type'] > 0) {
10959                 $this->_out('/TransformMethod /DocMDP');
10960                 $this->_out('/TransformParams');
10961                 $this->_out('<<');
10962                 $this->_out('/Type /TransformParams');
10963                 $this->_out('/V /1.2');
10964                 $this->_out('/P '.$this->signature_data['cert_type'].'');
10965             } else {
10966                 $this->_out('/TransformMethod /UR3');
10967                 $this->_out('/TransformParams');
10968                 $this->_out('<<');
10969                 $this->_out('/Type /TransformParams');
10970                 $this->_out('/V /2.2');
10971                 if (!$this->empty_string($this->ur_document)) {
10972                     $this->_out('/Document['.$this->ur_document.']');
10973                 }
10974                 if (!$this->empty_string($this->ur_annots)) {
10975                     $this->_out('/Annots['.$this->ur_annots.']');
10976                 }
10977                 if (!$this->empty_string($this->ur_form)) {
10978                     $this->_out('/Form['.$this->ur_form.']');
10979                 }
10980                 if (!$this->empty_string($this->ur_signature)) {
10981                     $this->_out('/Signature['.$this->ur_signature.']');
10982                 }
10983             }
10984             $this->_out('>>');
10985             $this->_out('>>');
10986             $this->_out(']');
10987             if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
10988                 $this->_out('/Name '.$this->_textstring($this->signature_data['info']['Name']).'');
10989             }
10990             if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
10991                 $this->_out('/Location '.$this->_textstring($this->signature_data['info']['Location']).'');
10992             }
10993             if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
10994                 $this->_out('/Reason '.$this->_textstring($this->signature_data['info']['Reason']).'');
10995             }
10996             if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
10997                 $this->_out('/ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']).'');
10998             }
10999             $this->_out('/M '.$this->_datestring());
11000         }
11001         
11002         /*
11003         * Set User's Rights for PDF Reader
11004         * WARNING: This works only using the Adobe private key with the setSignature() method!.
11005         * Check the PDF Reference 8.7.1 Transform Methods, 
11006         * Table 8.105 Entries in the UR transform parameters dictionary
11007         * @param boolean $enable if true enable user's rights on PDF reader
11008         * @param string $document Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data.
11009         * @param string $annots Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations.
11010         * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 
11011         * @param string $signature Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field.
11012         * @access public
11013         * @author Nicola Asuni
11014         * @since 2.9.000 (2008-03-26)
11015         */
11016         public function setUserRights(
11017                 $enable=true, 
11018                 $document='/FullSave',
11019                 $annots='/Create/Delete/Modify/Copy/Import/Export',
11020                 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
11021                 $signature='/Modify') {
11022             $this->ur = $enable;
11023             $this->ur_document = $document;
11024             $this->ur_annots = $annots;
11025             $this->ur_form = $form;
11026             $this->ur_signature = $signature;
11027             if (!$this->sign) {
11028                 // This signature only works using the Adobe Private key that is unavailable!
11029                 $this->setSignature('', '', '', '', 0, array());
11030             }
11031         }
11032         
11033         /*
11034         * Enable document signature (requires the OpenSSL Library).
11035         * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
11036         * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')
11037         * @param mixed $private_key private key (string or filename prefixed with 'file://')
11038         * @param string $private_key_password password
11039         * @param string $extracerts specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
11040         * @param int $cert_type The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
11041         * @parm array $info array of option information: Name, Location, Reason, ContactInfo.
11042         * @access public
11043         * @author Nicola Asuni
11044         * @since 4.6.005 (2009-04-24)
11045         */
11046         public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
11047             // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
11048             // to convert pfx certificate to pem: openssl
11049             //     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
11050             $this->sign = true;
11051             $this->signature_data = array();
11052             if (strlen($signing_cert) == 0) {
11053                 $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt';
11054                 $private_key_password = 'tcpdfdemo';
11055             }
11056             if (strlen($private_key) == 0) {
11057                 $private_key = $signing_cert;
11058             }
11059             $this->signature_data['signcert'] = $signing_cert;
11060             $this->signature_data['privkey'] = $private_key;
11061             $this->signature_data['password'] = $private_key_password;
11062             $this->signature_data['extracerts'] = $extracerts;
11063             $this->signature_data['cert_type'] = $cert_type;
11064             $this->signature_data['info'] = $info;
11065         }
11066         
11067         /*
11068         * Create a new page group.
11069         * NOTE: call this function before calling AddPage()
11070         * @param int $page starting group page (leave empty for next page).
11071         * @access public
11072         * @since 3.0.000 (2008-03-27)
11073         */
11074         public function startPageGroup($page='') {
11075             if (empty($page)) {
11076                 $page = $this->page + 1;
11077             }
11078             $this->newpagegroup[$page] = true;
11079         }
11080 
11089         public function AliasNbPages($alias='{nb}') {
11090             $this->AliasNbPages = $alias;
11091         }
11092         
11101         public function getAliasNbPages() {
11102             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11103                 return '{'.$this->AliasNbPages.'}';
11104             }
11105             return $this->AliasNbPages;
11106         }
11107 
11116         public function AliasNumPage($alias='{pnb}') {
11117             //Define an alias for total number of pages
11118             $this->AliasNumPage = $alias;
11119         }
11120         
11129         public function getAliasNumPage() {
11130             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11131                 return '{'.$this->AliasNumPage.'}';
11132             }
11133             return $this->AliasNumPage;
11134         }
11135         
11136         /*
11137         * Return the current page in the group.
11138         * @return current page in the group
11139         * @access public
11140         * @since 3.0.000 (2008-03-27)
11141         */
11142         public function getGroupPageNo() {
11143             return $this->pagegroups[$this->currpagegroup];
11144         }
11145 
11152         public function getGroupPageNoFormatted() {
11153             return $this->formatPageNumber($this->getGroupPageNo());
11154         }
11155         
11156         /*
11157          * Return the alias of the current page group
11158          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
11159          * (will be replaced by the total number of pages in this group).
11160          * @return alias of the current page group
11161          * @access public
11162          * @since 3.0.000 (2008-03-27)
11163         */
11164         public function getPageGroupAlias() {
11165             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11166                 return '{'.$this->currpagegroup.'}';
11167             }
11168             return $this->currpagegroup;
11169         }
11170         
11171         /*
11172          * Return the alias for the page number on the current page group
11173          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
11174          * (will be replaced by the total number of pages in this group).
11175          * @return alias of the current page group
11176          * @access public
11177          * @since 4.5.000 (2009-01-02)
11178         */
11179         public function getPageNumGroupAlias() {
11180             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11181                 return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
11182             }
11183             return str_replace('{nb', '{pnb', $this->currpagegroup);
11184         }
11185 
11193         protected function formatPageNumber($num) {
11194             return number_format((float)$num, 0, '', '.');
11195         }
11196 
11205         protected function formatTOCPageNumber($num) {
11206             return number_format((float)$num, 0, '', '.');
11207         }
11208 
11215         public function PageNoFormatted() {
11216             return $this->formatPageNumber($this->PageNo());
11217         }
11218 
11219         /*
11220         * Put visibility settings.
11221         * @access protected
11222         * @since 3.0.000 (2008-03-27)
11223         */
11224         protected function _putocg() {
11225             $this->_newobj();
11226             $this->n_ocg_print = $this->n;
11227             $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
11228             $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
11229             $this->_out('endobj');
11230             $this->_newobj();
11231             $this->n_ocg_view = $this->n;
11232             $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
11233             $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
11234             $this->_out('endobj');
11235         }
11236         
11237         /*
11238         * Set the visibility of the successive elements.
11239         * This can be useful, for instance, to put a background 
11240         * image or color that will show on screen but won't print.
11241         * @param string $v visibility mode. Legal values are: all, print, screen.
11242         * @access public
11243         * @since 3.0.000 (2008-03-27)
11244         */
11245         public function setVisibility($v) {
11246             if ($this->openMarkedContent) {
11247                 // close existing open marked-content
11248                 $this->_out('EMC');
11249                 $this->openMarkedContent = false;
11250             }
11251             switch($v) {
11252                 case 'print': {
11253                     $this->_out('/OC /OC1 BDC');
11254                     $this->openMarkedContent = true;
11255                     break;
11256                 }
11257                 case 'screen': {
11258                     $this->_out('/OC /OC2 BDC');
11259                     $this->openMarkedContent = true;
11260                     break;
11261                 }
11262                 case 'all': {
11263                     $this->_out('');
11264                     break;
11265                 }
11266                 default: {
11267                     $this->Error('Incorrect visibility: '.$v);
11268                     break;
11269                 }
11270             }
11271             $this->visibility = $v;
11272         }
11273         
11274         /*
11275         * Add transparency parameters to the current extgstate
11276         * @param array $params parameters
11277         * @return the number of extgstates
11278         * @access protected
11279         * @since 3.0.000 (2008-03-27)
11280         */
11281         protected function addExtGState($parms) {
11282             $n = count($this->extgstates) + 1;
11283             $this->extgstates[$n]['parms'] = $parms;
11284             return $n;
11285         }
11286         
11287         /*
11288         * Add an extgstate
11289         * @param array $gs extgstate
11290         * @access protected
11291         * @since 3.0.000 (2008-03-27)
11292         */
11293         protected function setExtGState($gs) {
11294             $this->_out(sprintf('/GS%d gs', $gs));
11295         }
11296         
11297         /*
11298         * Put extgstates for object transparency
11299         * @param array $gs extgstate
11300         * @access protected
11301         * @since 3.0.000 (2008-03-27)
11302         */
11303         protected function _putextgstates() {
11304             $ne = count($this->extgstates);
11305             for ($i = 1; $i <= $ne; ++$i) {
11306                 $this->_newobj();
11307                 $this->extgstates[$i]['n'] = $this->n;
11308                 $this->_out('<</Type /ExtGState');
11309                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
11310                     $this->_out('/'.$k.' '.$v);
11311                 }
11312                 $this->_out('>>');
11313                 $this->_out('endobj');
11314             }
11315         }
11316         
11317         /*
11318         * Set alpha for stroking (CA) and non-stroking (ca) operations.
11319         * @param float $alpha real value from 0 (transparent) to 1 (opaque)
11320         * @param string $bm blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
11321         * @access public
11322         * @since 3.0.000 (2008-03-27)
11323         */
11324         public function setAlpha($alpha, $bm='Normal') {
11325             $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
11326             $this->setExtGState($gs);
11327         }
11328 
11329         /*
11330         * Set the default JPEG compression quality (1-100)
11331         * @param int $quality JPEG quality, integer between 1 and 100
11332         * @access public
11333         * @since 3.0.000 (2008-03-27)
11334         */
11335         public function setJPEGQuality($quality) {
11336             if (($quality < 1) OR ($quality > 100)) {
11337                 $quality = 75;
11338             }
11339             $this->jpeg_quality = intval($quality);
11340         }
11341         
11342         /*
11343         * Set the default number of columns in a row for HTML tables.
11344         * @param int $cols number of columns
11345         * @access public
11346         * @since 3.0.014 (2008-06-04)
11347         */
11348         public function setDefaultTableColumns($cols=4) { 
11349             $this->default_table_columns = intval($cols); 
11350         }
11351         
11352         /*
11353         * Set the height of the cell (line height) respect the font height.
11354         * @param int $h cell proportion respect font height (typical value = 1.25).
11355         * @access public
11356         * @since 3.0.014 (2008-06-04)
11357         */
11358         public function setCellHeightRatio($h) { 
11359             $this->cell_height_ratio = $h; 
11360         }
11361         
11362         /*
11363         * return the height of cell repect font height.
11364         * @access public
11365         * @since 4.0.012 (2008-07-24)
11366         */
11367         public function getCellHeightRatio() { 
11368             return $this->cell_height_ratio; 
11369         }
11370         
11371         /*
11372         * Set the PDF version (check PDF reference for valid values).
11373         * Default value is 1.t
11374         * @access public
11375         * @since 3.1.000 (2008-06-09)
11376         */
11377         public function setPDFVersion($version='1.7') { 
11378             $this->PDFVersion = $version;
11379         }
11380         
11381         /*
11382         * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
11383         * (see Section 8.1 of PDF reference, "Viewer Preferences").
11384         * <ul>
11385         * <li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li>
11386         * <li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li>
11387         * <li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li>
11388         * <li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li>
11389         * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
11390         * <li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li>
11391         * <li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li><ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li>
11392         * <li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
11393         * <li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
11394         * <li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
11395         * <li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
11396         * <li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li><ul></li>
11397         * <li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li>
11398         * <li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li>
11399         * <li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li>
11400         * <li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li>
11401         * </ul>
11402         * @param array $preferences array of options.
11403         * @author Nicola Asuni
11404         * @access public
11405         * @since 3.1.000 (2008-06-09)
11406         */
11407         public function setViewerPreferences($preferences) { 
11408             $this->viewer_preferences = $preferences;
11409         }
11410         
11424         public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
11425             $this->Clip($x, $y, $w, $h);
11426             $this->Gradient(2, $col1, $col2, $coords);
11427         }
11428         
11442         public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
11443             $this->Clip($x, $y, $w, $h);
11444             $this->Gradient(3, $col1, $col2, $coords);
11445         }
11446         
11464         public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1) {
11465             $this->Clip($x, $y, $w, $h);        
11466             $n = count($this->gradients) + 1;
11467             $this->gradients[$n]['type'] = 6; //coons patch mesh
11468             //check the coords array if it is the simple array or the multi patch array
11469             if (!isset($coords[0]['f'])) {
11470                 //simple array -> convert to multi patch array
11471                 if (!isset($col1[1])) {
11472                     $col1[1] = $col1[2] = $col1[0];
11473                 }
11474                 if (!isset($col2[1])) {
11475                     $col2[1] = $col2[2] = $col2[0];
11476                 }
11477                 if (!isset($col3[1])) {
11478                     $col3[1] = $col3[2] = $col3[0];
11479                 }
11480                 if (!isset($col4[1])) {
11481                     $col4[1] = $col4[2] = $col4[0];
11482                 }
11483                 $patch_array[0]['f'] = 0;
11484                 $patch_array[0]['points'] = $coords;
11485                 $patch_array[0]['colors'][0]['r'] = $col1[0];
11486                 $patch_array[0]['colors'][0]['g'] = $col1[1];
11487                 $patch_array[0]['colors'][0]['b'] = $col1[2];
11488                 $patch_array[0]['colors'][1]['r'] = $col2[0];
11489                 $patch_array[0]['colors'][1]['g'] = $col2[1];
11490                 $patch_array[0]['colors'][1]['b'] = $col2[2];
11491                 $patch_array[0]['colors'][2]['r'] = $col3[0];
11492                 $patch_array[0]['colors'][2]['g'] = $col3[1];
11493                 $patch_array[0]['colors'][2]['b'] = $col3[2];
11494                 $patch_array[0]['colors'][3]['r'] = $col4[0];
11495                 $patch_array[0]['colors'][3]['g'] = $col4[1];
11496                 $patch_array[0]['colors'][3]['b'] = $col4[2];
11497             } else {
11498                 //multi patch array
11499                 $patch_array = $coords;
11500             }
11501             $bpcd = 65535; //16 BitsPerCoordinate
11502             //build the data stream
11503             $this->gradients[$n]['stream'] = '';
11504             $count_patch = count($patch_array);
11505             for ($i=0; $i < $count_patch; ++$i) {
11506                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
11507                 $count_points = count($patch_array[$i]['points']);
11508                 for ($j=0; $j < $count_points; ++$j) {
11509                     //each point as 16 bit
11510                     $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
11511                     if ($patch_array[$i]['points'][$j] < 0) {
11512                         $patch_array[$i]['points'][$j] = 0;
11513                     }
11514                     if ($patch_array[$i]['points'][$j] > $bpcd) {
11515                         $patch_array[$i]['points'][$j] = $bpcd;
11516                     }
11517                     $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
11518                     $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
11519                 }
11520                 $count_cols = count($patch_array[$i]['colors']);
11521                 for ($j=0; $j < $count_cols; ++$j) {
11522                     //each color component as 8 bit
11523                     $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
11524                     $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
11525                     $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
11526                 }
11527             }
11528             //paint the gradient
11529             $this->_out('/Sh'.$n.' sh');
11530             //restore previous Graphic State
11531             $this->_out('Q');
11532         }
11533         
11544         protected function Clip($x, $y, $w, $h) {
11545             if ($this->rtl) {
11546                 $x = $this->w - $x - $w;
11547             }
11548             //save current Graphic State
11549             $s = 'q';
11550             //set clipping area
11551             $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
11552             //set up transformation matrix for gradient
11553             $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
11554             $this->_out($s);
11555         }
11556                 
11567         protected function Gradient($type, $col1, $col2, $coords) {
11568             $n = count($this->gradients) + 1;
11569             $this->gradients[$n]['type'] = $type;
11570             if (!isset($col1[1])) {
11571                 $col1[1]=$col1[2]=$col1[0];
11572             }
11573             $this->gradients[$n]['col1'] = sprintf('%.3F %.3F %.3F', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
11574             if (!isset($col2[1])) {
11575                 $col2[1] = $col2[2] = $col2[0];
11576             }
11577             $this->gradients[$n]['col2'] = sprintf('%.3F %.3F %.3F', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
11578             $this->gradients[$n]['coords'] = $coords;
11579             //paint the gradient
11580             $this->_out('/Sh'.$n.' sh');
11581             //restore previous Graphic State
11582             $this->_out('Q');
11583         }
11584         
11591         function _putshaders() {
11592             foreach ($this->gradients as $id => $grad) {  
11593                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
11594                     $this->_newobj();
11595                     $this->_out('<<');
11596                     $this->_out('/FunctionType 2');
11597                     $this->_out('/Domain [0.0 1.0]');
11598                     $this->_out('/C0 ['.$grad['col1'].']');
11599                     $this->_out('/C1 ['.$grad['col2'].']');
11600                     $this->_out('/N 1');
11601                     $this->_out('>>');
11602                     $this->_out('endobj');
11603                     $f1 = $this->n;
11604                 }
11605                 $this->_newobj();
11606                 $this->_out('<<');
11607                 $this->_out('/ShadingType '.$grad['type']);
11608                 $this->_out('/ColorSpace /DeviceRGB');
11609                 if ($grad['type'] == 2) {
11610                     $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
11611                     $this->_out('/Function '.$f1.' 0 R');
11612                     $this->_out('/Extend [true true] ');
11613                     $this->_out('>>');
11614                 } elseif ($grad['type'] == 3) {
11615                     //x0, y0, r0, x1, y1, r1
11616                     //at this this time radius of inner circle is 0
11617                     $this->_out(sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
11618                     $this->_out('/Function '.$f1.' 0 R');
11619                     $this->_out('/Extend [true true] ');
11620                     $this->_out('>>');
11621                 } elseif ($grad['type'] == 6) {
11622                     $this->_out('/BitsPerCoordinate 16');
11623                     $this->_out('/BitsPerComponent 8');
11624                     $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
11625                     $this->_out('/BitsPerFlag 8');
11626                     $this->_out('/Length '.strlen($grad['stream']));
11627                     $this->_out('>>');
11628                     $this->_putstream($grad['stream']);
11629                 }
11630                 $this->_out('endobj');
11631                 $this->gradients[$id]['id'] = $this->n;
11632             }
11633         }
11634 
11641         protected function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
11642             $h = $this->h;
11643             $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1*$this->k, ($h-$y1)*$this->k, $x2*$this->k, ($h-$y2)*$this->k, $x3*$this->k, ($h-$y3)*$this->k));
11644         }
11645         
11661         public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
11662             if ($this->rtl) {
11663                 $xc = $this->w - $xc;
11664             }
11665             if ($cw) {
11666                 $d = $b;
11667                 $b = $o - $a;
11668                 $a = $o - $d;
11669             } else {
11670                 $b += $o;
11671                 $a += $o;
11672             }
11673             $a = ($a % 360) + 360;
11674             $b = ($b % 360) + 360;
11675             if ($a > $b) {
11676                 $b +=360;
11677             }
11678             $b = $b / 360 * 2 * M_PI;
11679             $a = $a / 360 * 2 * M_PI;
11680             $d = $b - $a;
11681             if ($d == 0 ) {
11682                 $d = 2 * M_PI;
11683             }
11684             $k = $this->k;
11685             $hp = $this->h;
11686             if ($style=='F') {
11687                 $op = 'f';
11688             } elseif ($style=='FD' or $style=='DF') {
11689                 $op = 'b';
11690             } else {
11691                 $op = 's';
11692             }
11693             if (sin($d/2)) {
11694                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
11695             }
11696             //first put the center
11697             $this->_out(sprintf('%.2F %.2F m', ($xc)*$k, ($hp-$yc)*$k));
11698             //put the first point
11699             $this->_out(sprintf('%.2F %.2F l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
11700             //draw the arc
11701             if ($d < (M_PI/2)) {
11702                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
11703             } else {
11704                 $b = $a + $d/4;
11705                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
11706                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
11707                 $a = $b;
11708                 $b = $a + $d/4;
11709                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
11710                 $a = $b;
11711                 $b = $a + $d/4;
11712                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b) );
11713                 $a = $b;
11714                 $b = $a + $d/4;
11715                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
11716             }
11717             //terminate drawing
11718             $this->_out($op);
11719         }
11720         
11739         public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0) {
11740             if ($x === '') {
11741                 $x = $this->x;
11742             }
11743             if ($y === '') {
11744                 $y = $this->y;
11745             }
11746             $k = $this->k;
11747             $data = file_get_contents($file);
11748             if ($data === false) {
11749                 $this->Error('EPS file not found: '.$file);
11750             }
11751             $regs = array();
11752             // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
11753             preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
11754             if (count($regs) > 1) {
11755                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
11756                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
11757                     $versexp = explode(' ', $version_str);
11758                     $version = (float)array_pop($versexp);
11759                     if ($version >= 9) {
11760                         $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
11761                     }
11762                 }
11763             }
11764             // strip binary bytes in front of PS-header
11765             $start = strpos($data, '%!PS-Adobe');
11766             if ($start > 0) {
11767                 $data = substr($data, $start);
11768             }
11769             // find BoundingBox params
11770             preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
11771             if (count($regs) > 1) {
11772                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
11773             } else {
11774                 $this->Error('No BoundingBox found in EPS file: '.$file);
11775             }
11776             $start = strpos($data, '%%EndSetup');
11777             if ($start === false) {
11778                 $start = strpos($data, '%%EndProlog');
11779             }
11780             if ($start === false) {
11781                 $start = strpos($data, '%%BoundingBox');
11782             }
11783             $data = substr($data, $start);
11784             $end = strpos($data, '%%PageTrailer');
11785             if ($end===false) {
11786                 $end = strpos($data, 'showpage');
11787             }
11788             if ($end) {
11789                 $data = substr($data, 0, $end);
11790             }
11791             if ($w > 0) {
11792                 $scale_x = $w / (($x2 - $x1) / $k);
11793                 if ($h > 0) {
11794                     $scale_y = $h / (($y2 - $y1) / $k);
11795                 } else {
11796                     $scale_y = $scale_x;
11797                     $h = ($y2 - $y1) / $k * $scale_y;
11798                 }
11799             } else {
11800                 if ($h > 0) {
11801                     $scale_y = $h / (($y2 - $y1) / $k);
11802                     $scale_x = $scale_y;
11803                     $w = ($x2-$x1) / $k * $scale_x;
11804                 } else {
11805                     $w = ($x2 - $x1) / $k;
11806                     $h = ($y2 - $y1) / $k;
11807                 }
11808             }
11809             // Check whether we need a new page first as this does not fit
11810             if ($this->checkPageBreak($h, $y)) {
11811                 $y = $this->GetY() + $this->cMargin;
11812             }
11813             // set bottomcoordinates
11814             $this->img_rb_y = $y + $h;
11815             // set alignment
11816             if ($this->rtl) {
11817                 if ($palign == 'L') {
11818                     $ximg = $this->lMargin;
11819                     // set right side coordinate
11820                     $this->img_rb_x = $ximg + $w;
11821                 } elseif ($palign == 'C') {
11822                     $ximg = ($this->w - $x - $w) / 2;
11823                     // set right side coordinate
11824                     $this->img_rb_x = $ximg + $w;
11825                 } else {
11826                     $ximg = $this->w - $x - $w;
11827                     // set left side coordinate
11828                     $this->img_rb_x = $ximg;
11829                 }
11830             } else {
11831                 if ($palign == 'R') {
11832                     $ximg = $this->w - $this->rMargin - $w;
11833                     // set left side coordinate
11834                     $this->img_rb_x = $ximg;
11835                 } elseif ($palign == 'C') {
11836                     $ximg = ($this->w - $x - $w) / 2;
11837                     // set right side coordinate
11838                     $this->img_rb_x = $ximg + $w;
11839                 } else {
11840                     $ximg = $x;
11841                     // set right side coordinate
11842                     $this->img_rb_x = $ximg + $w;
11843                 }
11844             }
11845             if ($useBoundingBox) {
11846                 $dx = $ximg * $k - $x1;
11847                 $dy = $y * $k - $y1;
11848             } else {
11849                 $dx = $ximg * $k;
11850                 $dy = $y * $k;
11851             }
11852             // save the current graphic state
11853             $this->_out('q'.$this->epsmarker);
11854             // translate
11855             $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
11856             // scale
11857             if (isset($scale_x)) {
11858                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
11859             }
11860             // handle pc/unix/mac line endings
11861             preg_match('/[\r\n]+/s', $data, $regs);
11862             $lines = explode($regs[0], $data);
11863             $u=0;
11864             $cnt = count($lines);
11865             for ($i=0; $i < $cnt; ++$i) {
11866                 $line = $lines[$i];
11867                 if (($line == '') OR ($line{0} == '%')) {
11868                     continue;
11869                 }
11870                 $len = strlen($line);
11871                 $chunks = explode(' ', $line);
11872                 $cmd = array_pop($chunks);
11873                 // RGB
11874                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
11875                     $b = array_pop($chunks); 
11876                     $g = array_pop($chunks); 
11877                     $r = array_pop($chunks);
11878                     $this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg!
11879                     continue;
11880                 }
11881                 switch ($cmd) {
11882                     case 'm':
11883                     case 'l':
11884                     case 'v':
11885                     case 'y':
11886                     case 'c':
11887                     case 'k':
11888                     case 'K':
11889                     case 'g':
11890                     case 'G':
11891                     case 's':
11892                     case 'S':
11893                     case 'J':
11894                     case 'j':
11895                     case 'w':
11896                     case 'M':
11897                     case 'd':
11898                     case 'n':
11899                     case 'v': {
11900                         $this->_out($line);
11901                         break;
11902                     }
11903                     case 'x': {// custom fill color
11904                         list($c,$m,$y,$k) = $chunks;
11905                         $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k');
11906                         break;
11907                     }
11908                     case 'X': { // custom stroke color
11909                         list($c,$m,$y,$k) = $chunks;
11910                         $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K');
11911                         break;
11912                     }
11913                     case 'Y':
11914                     case 'N':
11915                     case 'V':
11916                     case 'L':
11917                     case 'C': {
11918                         $line{$len-1} = strtolower($cmd);
11919                         $this->_out($line);
11920                         break;
11921                     }
11922                     case 'b':
11923                     case 'B': {
11924                         $this->_out($cmd . '*');
11925                         break;
11926                     }
11927                     case 'f':
11928                     case 'F': {
11929                         if ($u > 0) {
11930                             $isU = false;
11931                             $max = min($i+5, $cnt);
11932                             for ($j=$i+1; $j < $max; ++$j)
11933                               $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
11934                             if ($isU) {
11935                                 $this->_out('f*');
11936                             }
11937                         } else {
11938                             $this->_out('f*');
11939                         }
11940                         break;
11941                     }
11942                     case '*u': {
11943                         ++$u;
11944                         break;
11945                     }
11946                     case '*U': {
11947                         --$u;
11948                         break;
11949                     }
11950                 }
11951             }
11952             // restore previous graphic state
11953             $this->_out($this->epsmarker.'Q');
11954             if (!empty($border)) {
11955                 $bx = $x;
11956                 $by = $y;
11957                 $this->x = $x;
11958                 $this->y = $y;
11959                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
11960                 $this->x = $bx;
11961                 $this->y = $by;
11962             }
11963             if ($link) {
11964                 $this->Link($ximg, $y, $w, $h, $link, 0);
11965             }
11966             // set pointer to align the successive text/objects
11967             switch($align) {
11968                 case 'T':{
11969                     $this->y = $y;
11970                     $this->x = $this->img_rb_x;
11971                     break;
11972                 }
11973                 case 'M':{
11974                     $this->y = $y + round($h/2);
11975                     $this->x = $this->img_rb_x;
11976                     break;
11977                 }
11978                 case 'B':{
11979                     $this->y = $this->img_rb_y;
11980                     $this->x = $this->img_rb_x;
11981                     break;
11982                 }
11983                 case 'N':{
11984                     $this->SetY($this->img_rb_y);
11985                     break;
11986                 }
11987                 default:{
11988                     break;
11989                 }
11990             }
11991             $this->endlinex = $this->img_rb_x;
11992         }
11993         
11999         public function setBarcode($bc='') {
12000             $this->barcode = $bc;
12001         }
12002         
12009         public function getBarcode() {
12010             return $this->barcode;
12011         }
12012         
12028         public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
12029             if ($this->empty_string($code)) {
12030                 return;
12031             }
12032             require_once(dirname(__FILE__).'/barcodes.php');
12033             // save current graphic settings
12034             $gvars = $this->getGraphicVars();
12035             // create new barcode object
12036             $barcodeobj = new TCPDFBarcode($code, $type);
12037             $arrcode = $barcodeobj->getBarcodeArray();
12038             if ($arrcode === false) {
12039                 $this->Error('Error in 1D barcode string');
12040             }
12041             // set default values
12042             if (!isset($style['position'])) {
12043                 if ($this->rtl) {
12044                     $style['position'] = 'R';
12045                 } else {
12046                     $style['position'] = 'L';
12047                 }
12048             }
12049             if (!isset($style['padding'])) {
12050                 $style['padding'] = 0;
12051             }
12052             if (!isset($style['fgcolor'])) {
12053                 $style['fgcolor'] = array(0,0,0); // default black
12054             }
12055             if (!isset($style['bgcolor'])) {
12056                 $style['bgcolor'] = false; // default transparent
12057             }
12058             if (!isset($style['border'])) {
12059                 $style['border'] = false;
12060             }
12061             $fontsize = 0;
12062             if (!isset($style['text'])) {
12063                 $style['text'] = false;
12064             }
12065             if ($style['text'] AND isset($style['font'])) {
12066                 if (isset($style['fontsize'])) {
12067                     $fontsize = $style['fontsize'];
12068                 }
12069                 $this->SetFont($style['font'], '', $fontsize);
12070             }
12071             if (!isset($style['stretchtext'])) {
12072                 $style['stretchtext'] = 4;
12073             }
12074             // set foreground color
12075             $this->SetDrawColorArray($style['fgcolor']);
12076             $this->SetTextColorArray($style['fgcolor']);
12077             if ($this->empty_string($w) OR ($w <= 0)) {
12078                 if ($this->rtl) {
12079                     $w = $this->x - $this->lMargin;
12080                 } else {
12081                     $w = $this->w - $this->rMargin - $this->x;
12082                 }
12083             }
12084             if ($this->empty_string($x)) {
12085                 $x = $this->GetX();
12086             }
12087             if ($this->rtl) {
12088                 $x = $this->w - $x;
12089             }
12090             if ($this->empty_string($y)) {
12091                 $y = $this->GetY();
12092             }
12093             if ($this->empty_string($xres)) {
12094                 $xres = 0.4;
12095             }
12096             $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']);
12097             $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']);
12098             if ($this->empty_string($h) OR ($h <= 0)) {
12099                 $h = 10 + $extraspace;
12100             }
12101             if ($this->checkPageBreak($h)) {
12102                 $y = $this->y;
12103             }
12104             // maximum bar heigth
12105             $barh = $h - $extraspace;
12106             switch ($style['position']) {
12107                 case 'L': { // left
12108                     if ($this->rtl) {
12109                         $xpos = $x - $w;
12110                     } else {
12111                         $xpos = $x;
12112                     }
12113                     break;
12114                 }
12115                 case 'C': { // center
12116                     $xdiff = (($w - $fbw) / 2);
12117                     if ($this->rtl) {
12118                         $xpos = $x - $w + $xdiff;
12119                     } else {
12120                         $xpos = $x + $xdiff;
12121                     }
12122                     break;
12123                 }
12124                 case 'R': { // right
12125                     if ($this->rtl) {
12126                         $xpos = $x - $fbw;
12127                     } else {
12128                         $xpos = $x + $w - $fbw;
12129                     }
12130                     break;
12131                 }
12132                 case 'S': { // stretch
12133                     $fbw = $w;
12134                     $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw'];
12135                     if ($this->rtl) {
12136                         $xpos = $x - $w;
12137                     } else {
12138                         $xpos = $x;
12139                     }
12140                     break;
12141                 }
12142             }
12143             $xpos_rect = $xpos;
12144             $xpos = $xpos_rect + $style['padding'];
12145             $xpos_text = $xpos;
12146             // barcode is always printed in LTR direction
12147             $tempRTL = $this->rtl;
12148             $this->rtl = false;
12149             // print background color
12150             if ($style['bgcolor']) {
12151                 $this->Rect($xpos_rect, $y, $fbw, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
12152             } elseif ($style['border']) {
12153                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
12154             }
12155             // print bars
12156             if ($arrcode !== false) {
12157                 foreach ($arrcode['bcode'] as $k => $v) {
12158                     $bw = ($v['w'] * $xres);
12159                     if ($v['t']) {
12160                         // draw a vertical bar
12161                         $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']);
12162                         $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh  / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
12163                     }
12164                     $xpos += $bw;
12165                 }
12166             }
12167             // print text
12168             if ($style['text']) {
12169                 // print text
12170                 $this->x = $xpos_text;
12171                 $this->y = $y + $style['padding'] + $barh; 
12172                 $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']);
12173             }
12174             // restore original direction
12175             $this->rtl = $tempRTL;
12176             // restore previous settings
12177             $this->setGraphicVars($gvars);
12178             // set bottomcoordinates
12179             $this->img_rb_y = $y + $h;
12180             if ($this->rtl) {
12181                 // set left side coordinate
12182                 $this->img_rb_x = ($this->w - $x - $w);
12183             } else {
12184                 // set right side coordinate
12185                 $this->img_rb_x = $x + $w;
12186             }
12187             // set pointer to align the successive text/objects
12188             switch($align) {
12189                 case 'T':{
12190                     $this->y = $y;
12191                     $this->x = $this->img_rb_x;
12192                     break;
12193                 }
12194                 case 'M':{
12195                     $this->y = $y + round($h/2);
12196                     $this->x = $this->img_rb_x;
12197                     break;
12198                 }
12199                 case 'B':{
12200                     $this->y = $this->img_rb_y;
12201                     $this->x = $this->img_rb_x;
12202                     break;
12203                 }
12204                 case 'N':{
12205                     $this->SetY($this->img_rb_y);
12206                     break;
12207                 }
12208                 default:{
12209                     break;
12210                 }
12211             }
12212         }
12213         
12229         public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
12230             // convert old settings for the new write1DBarcode() function.
12231             $xres = 1 / $xres;
12232             $newstyle = array(
12233                 'position' => 'L',
12234                 'border' => false,
12235                 'padding' => 0,
12236                 'fgcolor' => array(0,0,0),
12237                 'bgcolor' => false,
12238                 'text' => true,
12239                 'font' => $font,
12240                 'fontsize' => 8,
12241                 'stretchtext' => 4
12242             );
12243             if ($style & 1) {
12244                 $newstyle['border'] = true;
12245             }
12246             if ($style & 2) {
12247                 $newstyle['bgcolor'] = false;
12248             }
12249             if ($style & 4) {
12250                 $newstyle['position'] = 'C';
12251             } elseif ($style & 8) {
12252                 $newstyle['position'] = 'L';
12253             } elseif ($style & 16) {
12254                 $newstyle['position'] = 'R';
12255             }
12256             if ($style & 128) {
12257                 $newstyle['text'] = true;
12258             }
12259             if ($style & 256) {
12260                 $newstyle['stretchtext'] = 4;
12261             }
12262             $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
12263         }
12264         
12279         public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') {
12280             if ($this->empty_string($code)) {
12281                 return;
12282             }
12283             require_once(dirname(__FILE__).'/2dbarcodes.php');
12284             // save current graphic settings
12285             $gvars = $this->getGraphicVars();
12286             // create new barcode object
12287             $barcodeobj = new TCPDF2DBarcode($code, $type);
12288             $arrcode = $barcodeobj->getBarcodeArray();
12289             if ($arrcode === false) {
12290                 $this->Error('Error in 2D barcode string');
12291             }
12292             // set default values
12293             if (!isset($style['padding'])) {
12294                 $style['padding'] = 0;
12295             }
12296             if (!isset($style['fgcolor'])) {
12297                 $style['fgcolor'] = array(0,0,0); // default black
12298             }
12299             if (!isset($style['bgcolor'])) {
12300                 $style['bgcolor'] = false; // default transparent
12301             }
12302             if (!isset($style['border'])) {
12303                 $style['border'] = false;
12304             }
12305             // set foreground color
12306             $this->SetDrawColorArray($style['fgcolor']);
12307             if ($this->empty_string($x)) {
12308                 $x = $this->GetX();
12309             }
12310             if ($this->rtl) {
12311                 $x = $this->w - $x;
12312             }
12313             if ($this->empty_string($y)) {
12314                 $y = $this->GetY();
12315             }
12316             if ($this->empty_string($w) OR ($w <= 0)) {
12317                 if ($this->rtl) {
12318                     $w = $x - $this->lMargin;
12319                 } else {
12320                     $w = $this->w - $this->rMargin - $x;
12321                 }
12322             }
12323             if ($this->empty_string($h) OR ($h <= 0)) {
12324                 // 2d barcodes are square by default
12325                 $h = $w;
12326             }
12327             if ($this->checkPageBreak($h)) {
12328                 $y = $this->y;
12329             }
12330             // calculate barcode size (excluding padding)
12331             $bw = $w - (2 * $style['padding']);
12332             $bh = $h - (2 * $style['padding']);
12333             // calculate starting coordinates
12334             if ($this->rtl) {
12335                 $xpos = $x - $w;
12336             } else {
12337                 $xpos = $x;
12338             }
12339             $xpos += $style['padding'];
12340             $ypos = $y + $style['padding'];
12341             // barcode is always printed in LTR direction
12342             $tempRTL = $this->rtl;
12343             $this->rtl = false;
12344             // print background color
12345             if ($style['bgcolor']) {
12346                 $this->Rect($x, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
12347             } elseif ($style['border']) {
12348                 $this->Rect($x, $y, $w, $h, 'D');
12349             }
12350             // print barcode cells
12351             if ($arrcode !== false) {
12352                 $rows = $arrcode['num_rows'];
12353                 $cols = $arrcode['num_cols'];
12354                 // calculate dimension of single barcode cell
12355                 $cw = $bw / $cols;
12356                 $ch = $bh / $rows;
12357                 // for each row
12358                 for ($r = 0; $r < $rows; ++$r) {
12359                     $xr = $xpos;
12360                     // for each column
12361                     for ($c = 0; $c < $cols; ++$c) {
12362                         if ($arrcode['bcode'][$r][$c] == 1) {
12363                             // draw a single barcode cell
12364                             $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']);
12365                         }
12366                         $xr += $cw;
12367                     }
12368                     $ypos += $ch;
12369                 }
12370             }
12371             // restore original direction
12372             $this->rtl = $tempRTL;
12373             // restore previous settings
12374             $this->setGraphicVars($gvars);
12375             // set bottomcoordinates
12376             $this->img_rb_y = $y + $h;
12377             if ($this->rtl) {
12378                 // set left side coordinate
12379                 $this->img_rb_x = ($this->w - $x - $w);
12380             } else {
12381                 // set right side coordinate
12382                 $this->img_rb_x = $x + $w;
12383             }
12384             // set pointer to align the successive text/objects
12385             switch($align) {
12386                 case 'T':{
12387                     $this->y = $y;
12388                     $this->x = $this->img_rb_x;
12389                     break;
12390                 }
12391                 case 'M':{
12392                     $this->y = $y + round($h/2);
12393                     $this->x = $this->img_rb_x;
12394                     break;
12395                 }
12396                 case 'B':{
12397                     $this->y = $this->img_rb_y;
12398                     $this->x = $this->img_rb_x;
12399                     break;
12400                 }
12401                 case 'N':{
12402                     $this->SetY($this->img_rb_y);
12403                     break;
12404                 }
12405                 default:{
12406                     break;
12407                 }
12408             }
12409         }
12410         
12426         public function getMargins() {
12427             $ret = array(
12428                 'left' => $this->lMargin,
12429                 'right' => $this->rMargin,
12430                 'top' => $this->tMargin,
12431                 'bottom' => $this->bMargin,
12432                 'header' => $this->header_margin,
12433                 'footer' => $this->footer_margin,
12434                 'cell' => $this->cMargin,
12435             );
12436             return $ret;
12437         }
12438         
12449         public function getOriginalMargins() {
12450             $ret = array(
12451                 'left' => $this->original_lMargin,
12452                 'right' => $this->original_rMargin
12453             );
12454             return $ret;
12455         }
12456         
12463         public function getFontSize() {
12464             return $this->FontSize;
12465         }
12466         
12473         public function getFontSizePt() {
12474             return $this->FontSizePt;
12475         }
12476 
12483         public function getFontFamily() {
12484             return $this->FontFamily;
12485         }
12486 
12493         public function getFontStyle() {
12494             return $this->FontStyle;
12495         }
12496         
12517         public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) {
12518             return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
12519         }
12520         
12529         protected function getHtmlDomArray($html) {
12530             // remove all unsupported tags (the line below lists all supported tags)
12531             $html = strip_tags($html, '<marker/><a><b><blockquote><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><i><img><input><label><li><ol><option><p><pre><select><small><span><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>');
12532             //replace some blank characters
12533             $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
12534             $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
12535             $html = preg_replace('@(\r\n|\r)@', "\n", $html);
12536             $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
12537             $html = strtr($html, $repTable);
12538             $offset = 0;
12539             while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
12540                 $html_a = substr($html, 0, $offset);
12541                 $html_b = substr($html, $offset, ($pos - $offset + 6));
12542                 while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
12543                     // preserve newlines on <pre> tag
12544                     $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
12545                 }
12546                 $html = $html_a.$html_b.substr($html, $pos + 6);
12547                 $offset = strlen($html_a.$html_b);
12548             }
12549             $offset = 0;
12550             while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
12551                 $html_a = substr($html, 0, $offset);
12552                 $html_b = substr($html, $offset, ($pos - $offset + 11));
12553                 while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
12554                     // preserve newlines on <textarea> tag
12555                     $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
12556                     $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
12557                 }
12558                 $html = $html_a.$html_b.substr($html, $pos + 11);
12559                 $offset = strlen($html_a.$html_b);
12560             }
12561             $html = preg_replace("'([\s]*)<option'si", "<option", $html);
12562             $html = preg_replace("'</option>([\s]*)'si", "</option>", $html);
12563             $offset = 0;
12564             while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
12565                 $html_a = substr($html, 0, $offset);
12566                 $html_b = substr($html, $offset, ($pos - $offset + 9));
12567                 while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
12568                     $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2\t\\4\r", $html_b);
12569                     $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2\r", $html_b);
12570                 }
12571                 $html = $html_a.$html_b.substr($html, $pos + 9);
12572                 $offset = strlen($html_a.$html_b);
12573             }
12574             $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
12575             $html = preg_replace("'([\s]+)</select>'si", "\" />", $html);
12576             $html = str_replace("\n", ' ', $html);
12577             // restore textarea newlines
12578             $html = str_replace('<TBR>', "\n", $html);
12579             // remove extra spaces from code
12580             $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '</\\1>', $html);
12581             $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html);
12582             $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
12583             $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
12584             $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
12585             $html = preg_replace('/<img/', ' <img', $html);
12586             $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span></span>', $html);
12587             $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
12588             $html = preg_replace('/<textarea([^>]*)>/xi', '<textarea\\1 value="', $html);
12589             $html = preg_replace('/<\/textarea>/', '" />', $html);
12590             // trim string
12591             $html = preg_replace('/^[\s]+/', '', $html);
12592             $html = preg_replace('/[\s]+$/', '', $html);
12593             // pattern for generic tag
12594             $tagpattern = '/(<[^>]+>)/';
12595             // explodes the string
12596             $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
12597             // count elements
12598             $maxel = count($a);
12599             $elkey = 0;
12600             $key = 0;
12601             // create an array of elements
12602             $dom = array();
12603             $dom[$key] = array();
12604             // set first void element
12605             $dom[$key]['tag'] = false;
12606             $dom[$key]['value'] = '';
12607             $dom[$key]['parent'] = 0;
12608             $dom[$key]['fontname'] = $this->FontFamily;
12609             $dom[$key]['fontstyle'] = $this->FontStyle;
12610             $dom[$key]['fontsize'] = $this->FontSizePt;
12611             $dom[$key]['bgcolor'] = false;
12612             $dom[$key]['fgcolor'] = $this->fgcolor;
12613             $dom[$key]['align'] = '';
12614             $dom[$key]['listtype'] = '';
12615             $dom[$key]['text-indent'] = 0;
12616             $thead = false; // true when we are inside the THEAD tag
12617             ++$key;
12618             $level = array();
12619             array_push($level, 0); // root
12620             while ($elkey < $maxel) {
12621                 $dom[$key] = array();
12622                 $element = $a[$elkey];
12623                 $dom[$key]['elkey'] = $elkey;
12624                 if (preg_match($tagpattern, $element)) {
12625                     // html tag
12626                     $element = substr($element, 1, -1);
12627                     // get tag name
12628                     preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
12629                     $tagname = strtolower($tag[1]);
12630                     // check if we are inside a table header
12631                     if ($tagname == 'thead') {
12632                         if ($element{0} == '/') {
12633                             $thead = false;
12634                         } else {
12635                             $thead = true;
12636                         }
12637                         ++$elkey;
12638                         continue;
12639                     }
12640                     $dom[$key]['tag'] = true;
12641                     $dom[$key]['value'] = $tagname;
12642                     if ($element{0} == '/') {
12643                         // closing html tag
12644                         $dom[$key]['opening'] = false;
12645                         $dom[$key]['parent'] = end($level);
12646                         array_pop($level);
12647                         $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
12648                         $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
12649                         $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
12650                         $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
12651                         $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
12652                         $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
12653                         if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
12654                             $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
12655                         }
12656                         // set the number of columns in table tag
12657                         if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
12658                             $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
12659                         }
12660                         if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
12661                             $dom[($dom[$key]['parent'])]['content'] = '';
12662                             for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
12663                                 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
12664                             }
12665                             $key = $i;
12666                         }
12667                         // store header rows on a new table
12668                         if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
12669                             if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
12670                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
12671                             }
12672                             for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
12673                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
12674                             }
12675                         }
12676                         if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
12677                             $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
12678                         }
12679                     } else {
12680                         // opening html tag
12681                         $dom[$key]['opening'] = true;
12682                         $dom[$key]['parent'] = end($level);
12683                         if (substr($element, -1, 1) != '/') {
12684                             // not self-closing tag
12685                             array_push($level, $key);
12686                             $dom[$key]['self'] = false;
12687                         } else {
12688                             $dom[$key]['self'] = true;
12689                         }
12690                         // copy some values from parent
12691                         $parentkey = 0;
12692                         if ($key > 0) {
12693                             $parentkey = $dom[$key]['parent'];
12694                             $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
12695                             $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
12696                             $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
12697                             $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
12698                             $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
12699                             $dom[$key]['align'] = $dom[$parentkey]['align'];
12700                             $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
12701                             $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
12702                         }
12703                         // get attributes
12704                         preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER);
12705                         $dom[$key]['attribute'] = array(); // reset attribute array
12706                         while (list($id, $name) = each($attr_array[1])) {
12707                             $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
12708                         }
12709                         // split style attributes
12710                         if (isset($dom[$key]['attribute']['style'])) {
12711                             // get style attributes
12712                             preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
12713                             $dom[$key]['style'] = array(); // reset style attribute array
12714                             while (list($id, $name) = each($style_array[1])) {
12715                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
12716                             }
12717                             // --- get some style attributes ---
12718                             if (isset($dom[$key]['style']['font-family'])) {
12719                                 // font family
12720                                 if (isset($dom[$key]['style']['font-family'])) {
12721                                     $fontslist = preg_split('/[,]/', strtolower($dom[$key]['style']['font-family']));
12722                                     foreach ($fontslist as $font) {
12723                                         $font = trim(strtolower($font));
12724                                         if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
12725                                             $dom[$key]['fontname'] = $font;
12726                                             break;
12727                                         }
12728                                     }
12729                                 }
12730                             }
12731                             // list-style-type
12732                             if (isset($dom[$key]['style']['list-style-type'])) {
12733                                 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
12734                                 if ($dom[$key]['listtype'] == 'inherit') {
12735                                     $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
12736                                 }
12737                             }
12738                             // text-indent
12739                             if (isset($dom[$key]['style']['text-indent'])) {
12740                                 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
12741                                 if ($dom[$key]['text-indent'] == 'inherit') {
12742                                     $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
12743                                 }
12744                             }
12745                             // font size
12746                             if (isset($dom[$key]['style']['font-size'])) {
12747                                 $fsize = trim($dom[$key]['style']['font-size']);
12748                                 switch ($fsize) {
12749                                     // absolute-size
12750                                     case 'xx-small': {
12751                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
12752                                         break;
12753                                     }
12754                                     case 'x-small': {
12755                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
12756                                         break;
12757                                     }
12758                                     case 'small': {
12759                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
12760                                         break;
12761                                     }
12762                                     case 'medium': {
12763                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'];
12764                                         break;
12765                                     }
12766                                     case 'large': {
12767                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
12768                                         break;
12769                                     }
12770                                     case 'x-large': {
12771                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
12772                                         break;
12773                                     }
12774                                     case 'xx-large': {
12775                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
12776                                         break;
12777                                     }
12778                                     // relative-size
12779                                     case 'smaller': {
12780                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
12781                                         break;
12782                                     }
12783                                     case 'larger': {
12784                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
12785                                         break;
12786                                     }
12787                                     default: {
12788                                         $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
12789                                     }
12790                                 }
12791                             }
12792                             // font style
12793                             if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
12794                                 $dom[$key]['fontstyle'] .= 'B';
12795                             }
12796                             if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
12797                                 $dom[$key]['fontstyle'] .= '"I';
12798                             }
12799                             // font color
12800                             if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
12801                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
12802                             }
12803                             // background color
12804                             if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
12805                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
12806                             }
12807                             // text-decoration
12808                             if (isset($dom[$key]['style']['text-decoration'])) {
12809                                 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
12810                                 foreach ($decors as $dec) {
12811                                     $dec = trim($dec);
12812                                     if (!$this->empty_string($dec)) {
12813                                         if ($dec{0} == 'u') {
12814                                             $dom[$key]['fontstyle'] .= 'U';
12815                                         } elseif ($dec{0} == 'l') {
12816                                             $dom[$key]['fontstyle'] .= 'D';
12817                                         }
12818                                     }
12819                                 }
12820                             }
12821                             // check for width attribute
12822                             if (isset($dom[$key]['style']['width'])) {
12823                                 $dom[$key]['width'] = $dom[$key]['style']['width'];
12824                             }
12825                             // check for height attribute
12826                             if (isset($dom[$key]['style']['height'])) {
12827                                 $dom[$key]['height'] = $dom[$key]['style']['height'];
12828                             }
12829                             // check for text alignment
12830                             if (isset($dom[$key]['style']['text-align'])) {
12831                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
12832                             }
12833                             // check for border attribute
12834                             if (isset($dom[$key]['style']['border'])) {
12835                                 $dom[$key]['attribute']['border'] = $dom[$key]['style']['border'];
12836                             }
12837                         }
12838                         // check for font tag
12839                         if ($dom[$key]['value'] == 'font') {
12840                             // font family
12841                             if (isset($dom[$key]['attribute']['face'])) {
12842                                 $fontslist = preg_split('/[,]/', strtolower($dom[$key]['attribute']['face']));
12843                                 foreach ($fontslist as $font) {
12844                                     $font = trim(strtolower($font));
12845                                     if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
12846                                         $dom[$key]['fontname'] = $font;
12847                                         break;
12848                                     }
12849                                 }
12850                             }
12851                             // font size
12852                             if (isset($dom[$key]['attribute']['size'])) {
12853                                 if ($key > 0) {
12854                                     if ($dom[$key]['attribute']['size']{0} == '+') {
12855                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
12856                                     } elseif ($dom[$key]['attribute']['size']{0} == '-') {
12857                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
12858                                     } else {
12859                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
12860                                     }
12861                                 } else {
12862                                     $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
12863                                 }
12864                             }
12865                         }
12866                         // force natural alignment for lists
12867                         if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
12868                             AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
12869                             if ($this->rtl) {
12870                                 $dom[$key]['align'] = 'R';
12871                             } else {
12872                                 $dom[$key]['align'] = 'L';
12873                             }
12874                         }
12875                         if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
12876                             $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
12877                         }
12878                         if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
12879                             $dom[$key]['fontstyle'] .= 'B';
12880                         }
12881                         if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
12882                             $dom[$key]['fontstyle'] .= 'I';
12883                         }
12884                         if ($dom[$key]['value'] == 'u') {
12885                             $dom[$key]['fontstyle'] .= 'U';
12886                         }
12887                         if ($dom[$key]['value'] == 'del') {
12888                             $dom[$key]['fontstyle'] .= 'D';
12889                         }
12890                         if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
12891                             $dom[$key]['fontname'] = $this->default_monospaced_font;
12892                         }
12893                         if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
12894                             $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
12895                             $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
12896                             $dom[$key]['fontstyle'] .= 'B';
12897                         }
12898                         if (($dom[$key]['value'] == 'table')) {
12899                             $dom[$key]['rows'] = 0; // number of rows
12900                             $dom[$key]['trids'] = array(); // IDs of TR elements
12901                             $dom[$key]['thead'] = ''; // table header rows
12902                         }
12903                         if (($dom[$key]['value'] == 'tr')) {
12904                             $dom[$key]['cols'] = 0;
12905                             // store the number of rows on table element
12906                             ++$dom[($dom[$key]['parent'])]['rows'];
12907                             // store the TR elements IDs on table element
12908                             array_push($dom[($dom[$key]['parent'])]['trids'], $key);
12909                             if ($thead) {
12910                                 $dom[$key]['thead'] = true;
12911                             } else {
12912                                 $dom[$key]['thead'] = false;
12913                             }
12914                         }
12915                         if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
12916                             if (isset($dom[$key]['attribute']['colspan'])) {
12917                                 $colspan = intval($dom[$key]['attribute']['colspan']);
12918                             } else {
12919                                 $colspan = 1;
12920                             }
12921                             $dom[$key]['attribute']['colspan'] = $colspan;
12922                             $dom[($dom[$key]['parent'])]['cols'] += $colspan;
12923                         }
12924                         // set foreground color attribute
12925                         if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
12926                             $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
12927                         }
12928                         // set background color attribute
12929                         if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
12930                             $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
12931                         }
12932                         // check for width attribute
12933                         if (isset($dom[$key]['attribute']['width'])) {
12934                             $dom[$key]['width'] = $dom[$key]['attribute']['width'];
12935                         }
12936                         // check for height attribute
12937                         if (isset($dom[$key]['attribute']['height'])) {
12938                             $dom[$key]['height'] = $dom[$key]['attribute']['height'];
12939                         }
12940                         // check for text alignment
12941                         if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
12942                             $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
12943                         }
12944                     } // end opening tag
12945                 } else {
12946                     // text
12947                     $dom[$key]['tag'] = false;
12948                     $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
12949                     $dom[$key]['parent'] = end($level);
12950                 }
12951                 ++$elkey;
12952                 ++$key;
12953             }
12954             return $dom;
12955         }
12956         
12969         public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
12970             $gvars = $this->getGraphicVars();
12971             // store current values
12972             $prevPage = $this->page;
12973             $prevlMargin = $this->lMargin;
12974             $prevrMargin = $this->rMargin;
12975             $curfontname = $this->FontFamily;
12976             $curfontstyle = $this->FontStyle;
12977             $curfontsize = $this->FontSizePt;   
12978             $this->newline = true;
12979             $startlinepage = $this->page;
12980             $minstartliney = $this->y;
12981             $startlinex = $this->x;
12982             $startliney = $this->y;
12983             $yshift = 0;
12984             $newline = true;
12985             $loop = 0;
12986             $curpos = 0;
12987             $this_method_vars = array();
12988             $undo = false;
12989             $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul','tcpdf');
12990             $this->premode = false;
12991             if (isset($this->PageAnnots[$this->page])) {
12992                 $pask = count($this->PageAnnots[$this->page]);
12993             } else {
12994                 $pask = 0;
12995             }
12996             if (isset($this->footerlen[$this->page])) {
12997                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
12998             } else {
12999                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
13000             }
13001             $startlinepos = $this->footerpos[$this->page];
13002             $lalign = $align;
13003             $plalign = $align;
13004             if ($this->rtl) {
13005                 $w = $this->x - $this->lMargin;
13006             } else {
13007                 $w = $this->w - $this->rMargin - $this->x;
13008             }
13009             $w -= (2 * $this->cMargin);
13010             if ($cell) {
13011                 if ($this->rtl) {
13012                     $this->x -= $this->cMargin;
13013                 } else {
13014                     $this->x += $this->cMargin;
13015                 }
13016             }
13017             if ($this->customlistindent >= 0) {
13018                 $this->listindent = $this->customlistindent;
13019             } else {
13020                 $this->listindent = $this->GetStringWidth('0000');
13021             }
13022             // save previous states
13023             $prev_listnum = $this->listnum;
13024             $prev_listordered = $this->listordered;
13025             $prev_listcount = $this->listcount;
13026             $prev_lispacer = $this->lispacer;
13027             $this->listnum = 0;
13028             $this->listordered = array();
13029             $this->listcount = array();
13030             $this->lispacer = '';
13031             if (($this->empty_string($this->lasth)) OR ($reseth)) {
13032                 //set row height
13033                 $this->lasth = $this->FontSize * $this->cell_height_ratio; 
13034             }
13035             $dom = $this->getHtmlDomArray($html);
13036             $maxel = count($dom);
13037             $key = 0;
13038             while ($key < $maxel) {
13039                 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
13040                     if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
13041                         $dom[$key]['attribute']['nobr'] = false;
13042                     } else {
13043                         // store current object
13044                         $this->startTransaction();
13045                         // save this method vars
13046                         $this_method_vars['html'] = $html;
13047                         $this_method_vars['ln'] = $ln;
13048                         $this_method_vars['fill'] = $fill;
13049                         $this_method_vars['reseth'] = $reseth;
13050                         $this_method_vars['cell'] = $cell;
13051                         $this_method_vars['align'] = $align;
13052                         $this_method_vars['gvars'] = $gvars;
13053                         $this_method_vars['prevPage'] = $prevPage;
13054                         $this_method_vars['prevlMargin'] = $prevlMargin;
13055                         $this_method_vars['prevrMargin'] = $prevrMargin;
13056                         $this_method_vars['curfontname'] = $curfontname;
13057                         $this_method_vars['curfontstyle'] = $curfontstyle;
13058                         $this_method_vars['curfontsize'] = $curfontsize;
13059                         $this_method_vars['minstartliney'] = $minstartliney;
13060                         $this_method_vars['yshift'] = $yshift;
13061                         $this_method_vars['startlinepage'] = $startlinepage;
13062                         $this_method_vars['startlinepos'] = $startlinepos;
13063                         $this_method_vars['startlinex'] = $startlinex;
13064                         $this_method_vars['startliney'] = $startliney;
13065                         $this_method_vars['newline'] = $newline;
13066                         $this_method_vars['loop'] = $loop;
13067                         $this_method_vars['curpos'] = $curpos;
13068                         $this_method_vars['pask'] = $pask;
13069                         $this_method_vars['lalign'] = $lalign;
13070                         $this_method_vars['plalign'] = $plalign;
13071                         $this_method_vars['w'] = $w;
13072                         $this_method_vars['prev_listnum'] = $prev_listnum;
13073                         $this_method_vars['prev_listordered'] = $prev_listordered;
13074                         $this_method_vars['prev_listcount'] = $prev_listcount;
13075                         $this_method_vars['prev_lispacer'] = $prev_lispacer;
13076                         $this_method_vars['key'] = $key;
13077                         $this_method_vars['dom'] = $dom;
13078                     }
13079                 }
13080                 if ($dom[$key]['tag'] OR ($key == 0)) {
13081                     if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
13082                         $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
13083                     }
13084                     // vertically align image in line
13085                     if ((!$this->newline)
13086                         AND ($dom[$key]['value'] == 'img')
13087                         AND (isset($dom[$key]['attribute']['height']))
13088                         AND ($dom[$key]['attribute']['height'] > 0)) {
13089                         
13090                         // get image height
13091                         $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px');
13092                         if (!$this->InFooter) {
13093                             // check for page break
13094                             $this->checkPageBreak($imgh);
13095                         }
13096                         if ($this->page > $startlinepage) {
13097                             // fix line splitted over two pages
13098                             if (isset($this->footerlen[$startlinepage])) {
13099                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
13100                             }
13101                             // line to be moved one page forward
13102                             $pagebuff = $this->getPageBuffer($startlinepage);
13103                             $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
13104                             $tstart = substr($pagebuff, 0, $startlinepos);
13105                             $tend = substr($this->getPageBuffer($startlinepage), $curpos);
13106                             // remove line from previous page
13107                             $this->setPageBuffer($startlinepage, $tstart.''.$tend);
13108                             $pagebuff = $this->getPageBuffer($this->page);
13109                             $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
13110                             $tend = substr($pagebuff, $this->cntmrk[$this->page]);
13111                             // add line start to current page
13112                             $yshift = $minstartliney - $this->y;
13113                             $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
13114                             $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
13115                             // shift the annotations and links
13116                             if (isset($this->PageAnnots[$this->page])) {
13117                                 $next_pask = count($this->PageAnnots[$this->page]);
13118                             } else {
13119                                 $next_pask = 0;
13120                             }
13121                             if (isset($this->PageAnnots[$startlinepage])) {
13122                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
13123                                     if ($pak >= $pask) {
13124                                         $this->PageAnnots[$this->page][] = $pac;
13125                                         unset($this->PageAnnots[$startlinepage][$pak]);
13126                                         $npak = count($this->PageAnnots[$this->page]) - 1;
13127                                         $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;                                      
13128                                     }
13129                                 }
13130                                 
13131                             }
13132                             $pask = $next_pask;
13133                             $startlinepos = $this->cntmrk[$this->page];
13134                             $startlinepage = $this->page;
13135                             $startliney = $this->y;
13136                         }
13137                         $this->y += (($curfontsize / $this->k) - $imgh);
13138                         $minstartliney = min($this->y, $minstartliney); 
13139                     } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
13140                         // account for different font size
13141                         $pfontname = $curfontname;
13142                         $pfontstyle = $curfontstyle;
13143                         $pfontsize = $curfontsize;
13144                         $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
13145                         $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
13146                         $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
13147                         if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
13148                             $this->SetFont($fontname, $fontstyle, $fontsize);
13149                             $this->lasth = $this->FontSize * $this->cell_height_ratio;
13150                             if (is_numeric($fontsize) AND ($fontsize > 0)
13151                                 AND is_numeric($curfontsize) AND ($curfontsize > 0)
13152                                 AND ($fontsize != $curfontsize) AND (!$this->newline)
13153                                 AND ($key < ($maxel - 1))
13154                                 ) {
13155                                 if ((!$this->newline) AND ($this->page > $startlinepage)) {
13156                                     // fix lines splitted over two pages
13157                                     if (isset($this->footerlen[$startlinepage])) {
13158                                         $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
13159                                     }
13160                                     // line to be moved one page forward
13161                                     $pagebuff = $this->getPageBuffer($startlinepage);
13162                                     $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
13163                                     $tstart = substr($pagebuff, 0, $startlinepos);
13164                                     $tend = substr($this->getPageBuffer($startlinepage), $curpos);
13165                                     // remove line start from previous page
13166                                     $this->setPageBuffer($startlinepage, $tstart.''.$tend);
13167                                     $pagebuff = $this->getPageBuffer($this->page);
13168                                     $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
13169                                     $tend = substr($pagebuff, $this->cntmrk[$this->page]);
13170                                     // add line start to current page
13171                                     $yshift = $minstartliney - $this->y;
13172                                     $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
13173                                     $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
13174                                     // shift the annotations and links
13175                                     if (isset($this->PageAnnots[$this->page])) {
13176                                         $next_pask = count($this->PageAnnots[$this->page]);
13177                                     } else {
13178                                         $next_pask = 0;
13179                                     }
13180                                     if (isset($this->PageAnnots[$startlinepage])) {
13181                                         foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
13182                                             if ($pak >= $pask) {
13183                                                 $this->PageAnnots[$this->page][] = $pac;
13184                                                 unset($this->PageAnnots[$startlinepage][$pak]);
13185                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
13186                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
13187                                             }
13188                                         }
13189                                     }
13190                                     $pask = $next_pask;
13191                                 }
13192                                 if (($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
13193                                     $this->y += (($curfontsize - $fontsize) / $this->k);
13194                                 }
13195                                 $minstartliney = min($this->y, $minstartliney);
13196                             }
13197                             $curfontname = $fontname;
13198                             $curfontstyle = $fontstyle;
13199                             $curfontsize = $fontsize;
13200                         }
13201                     }
13202                     if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) {
13203                         $plalign = '';
13204                     }
13205                     // get current position on page buffer
13206                     $curpos = $this->pagelen[$startlinepage];
13207                     if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
13208                         $this->SetFillColorArray($dom[$key]['bgcolor']);
13209                         $wfill = true;
13210                     } else {
13211                         $wfill = $fill | false;
13212                     }
13213                     if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
13214                         $this->SetTextColorArray($dom[$key]['fgcolor']);
13215                     }
13216                     if (isset($dom[$key]['align'])) {
13217                         $lalign = $dom[$key]['align'];
13218                     }
13219                     if ($this->empty_string($lalign)) {
13220                         $lalign = $align;
13221                     }
13222                 }
13223                 // align lines
13224                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
13225                     $newline = true;
13226                     // we are at the beginning of a new line
13227                     if (isset($startlinex)) {
13228                         $yshift = $minstartliney - $startliney;
13229                         if (($yshift > 0) OR ($this->page > $startlinepage)) {
13230                             $yshift = 0;
13231                         }
13232                         if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
13233                             // the last line must be shifted to be aligned as requested
13234                             $linew = abs($this->endlinex - $startlinex);
13235                             $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
13236                             if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
13237                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
13238                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
13239                             } elseif (isset($opentagpos)) {
13240                                 $midpos = $opentagpos;
13241                             } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
13242                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
13243                                 $midpos = $this->footerpos[$startlinepage];
13244                             } else {
13245                                 $midpos = 0;
13246                             }
13247                             if ($midpos > 0) {
13248                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
13249                                 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
13250                             } else {
13251                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
13252                                 $pend = '';
13253                             }
13254                             // calculate shifting amount
13255                             $tw = $w;
13256                             if ($this->lMargin != $prevlMargin) {
13257                                 $tw += ($prevlMargin - $this->lMargin);
13258                             }
13259                             if ($this->rMargin != $prevrMargin) {
13260                                 $tw += ($prevrMargin - $this->rMargin);
13261                             }
13262                             $mdiff = abs($tw - $linew);
13263                             $t_x = 0;
13264                             if ($plalign == 'C') {
13265                                 if ($this->rtl) {
13266                                     $t_x = -($mdiff / 2);
13267                                 } else {
13268                                     $t_x = ($mdiff / 2);
13269                                 }
13270                             } elseif (($plalign == 'R') AND (!$this->rtl)) {
13271                                 // right alignment on LTR document
13272                                 $t_x = $mdiff;  
13273                             } elseif (($plalign == 'L') AND ($this->rtl)) {
13274                                 // left alignment on RTL document
13275                                 $t_x = -$mdiff;
13276                             } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
13277                                 // Justification
13278                                 if ($this->rtl OR $this->tmprtl) {
13279                                     $t_x = $this->lMargin - $this->endlinex;
13280                                 }
13281                                 $no = 0;
13282                                 $ns = 0;
13283                                 $pmidtemp = $pmid;
13284                                 // escape special characters
13285                                 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
13286                                 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
13287                                 // search spaces
13288                                 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
13289                                     $maxkk = count($lnstring[1]) - 1;
13290                                     for ($kk=0; $kk <= $maxkk; ++$kk) {
13291                                         // restore special characters
13292                                         $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
13293                                         $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
13294                                         if ($kk == $maxkk) {
13295                                             if ($this->rtl OR $this->tmprtl) {
13296                                                 $tvalue = ltrim($lnstring[1][$kk]);
13297                                             } else {
13298                                                 $tvalue = rtrim($lnstring[1][$kk]);
13299                                             }
13300                                         } else {
13301                                             $tvalue = $lnstring[1][$kk];
13302                                         }
13303                                         // count spaces on line
13304                                         $no += substr_count($lnstring[1][$kk], chr(32));
13305                                         $ns += substr_count($tvalue, chr(32));
13306                                     }
13307                                     if ($this->rtl OR $this->tmprtl) {
13308                                         $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32)));
13309                                     }
13310                                     // calculate additional space to add to each space
13311                                     $spacelen = $this->GetStringWidth(chr(32));
13312                                     $spacewidth = (($tw - $linew + (($no - $ns) * $spacelen)) / ($ns?$ns:1)) * $this->k;
13313                                     $spacewidthu = -1000 * ($tw - $linew + ($no * $spacelen)) / ($ns?$ns:1) / $this->FontSize;
13314                                     $nsmax = $ns;
13315                                     $ns = 0;
13316                                     reset($lnstring);
13317                                     $offset = 0;
13318                                     $strcount = 0;
13319                                     $prev_epsposbeg = 0;
13320                                     global $spacew;
13321                                     while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
13322                                         // check if we are inside a string section '[( ... )]'
13323                                         $stroffset = strpos($pmid, '[(', $offset);
13324                                         if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
13325                                             // set offset to the end of string section 
13326                                             $offset = strpos($pmid, ')]', $stroffset);
13327                                             while (($offset !== false) AND ($pmid{($offset - 1)} == '\\')) {
13328                                                 $offset = strpos($pmid, ')]', ($offset + 1));
13329                                             }
13330                                             if ($offset === false) {
13331                                                 $this->Error('HTML Justification: malformed PDF code.');
13332                                             }
13333                                             continue;
13334                                         }
13335                                         if ($this->rtl OR $this->tmprtl) {
13336                                             $spacew = ($spacewidth * ($nsmax - $ns));
13337                                         } else {
13338                                             $spacew = ($spacewidth * $ns);
13339                                         }
13340                                         $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
13341                                         $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
13342                                         $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
13343                                         if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
13344                                             OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
13345                                             // shift EPS images
13346                                             $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
13347                                             $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
13348                                             $pmid_b = substr($pmid, 0, $epsposbeg);
13349                                             $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
13350                                             $pmid_e = substr($pmid, $epsposend);
13351                                             $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
13352                                             $offset = $epsposend;
13353                                             continue;
13354                                         }
13355                                         $prev_epsposbeg = $epsposbeg;
13356                                         $currentxpos = 0;
13357                                         // shift blocks of code
13358                                         switch ($strpiece[2][0]) {
13359                                             case 'Td':
13360                                             case 'cm':
13361                                             case 'm':
13362                                             case 'l': {
13363                                                 // get current X position
13364                                                 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
13365                                                 $currentxpos = $xmatches[1];
13366                                                 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
13367                                                     if ($strcount == $maxkk) {
13368                                                         if ($this->rtl OR $this->tmprtl) {
13369                                                             $tvalue = $lnstring[1][$strcount];
13370                                                         } else {
13371                                                             $tvalue = rtrim($lnstring[1][$strcount]);
13372                                                         }
13373                                                     } else {
13374                                                         $tvalue = $lnstring[1][$strcount];
13375                                                     }
13376                                                     $ns += substr_count($tvalue, chr(32));
13377                                                     ++$strcount;
13378                                                 }
13379                                                 if ($this->rtl OR $this->tmprtl) {
13380                                                     $spacew = ($spacewidth * ($nsmax - $ns));
13381                                                 }
13382                                                 // justify block
13383                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
13384                                                     create_function('$matches', 'global $spacew;
13385                                                     $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
13386                                                     return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
13387                                                 break;
13388                                             }
13389                                             case 're': {
13390                                                 // get current X position
13391                                                 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
13392                                                 $currentxpos = $xmatches[1];
13393                                                 // justify block
13394                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
13395                                                     create_function('$matches', 'global $spacew;
13396                                                     $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
13397                                                     return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
13398                                                 break;
13399                                             }
13400                                             case 'c': {
13401                                                 // get current X position
13402                                                 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
13403                                                 $currentxpos = $xmatches[1];
13404                                                 // justify block
13405                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
13406                                                     create_function('$matches', 'global $spacew;
13407                                                     $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
13408                                                     $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
13409                                                     $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
13410                                                     return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
13411                                                 break;
13412                                             }
13413                                         }
13414                                         // shift the annotations and links
13415                                         if (isset($this->PageAnnots[$this->page])) {
13416                                             foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
13417                                                 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
13418                                                     $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
13419                                                     $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
13420                                                     break;
13421                                                 }
13422                                             }
13423                                         }
13424                                     } // end of while
13425                                     // remove markers
13426                                     $pmid = str_replace('x*#!#*x', '', $pmid);
13427                                     if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
13428                                         // multibyte characters
13429                                         $spacew = $spacewidthu;
13430                                         $pmidtemp = $pmid;
13431                                         // escape special characters
13432                                         $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
13433                                         $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
13434                                         $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
13435                                                     create_function('$matches', 'global $spacew;
13436                                                     $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
13437                                                     $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
13438                                                     return "[(".str_replace(chr(0).chr(32), ") ".($spacew)." (", $matches[1]).")]";'), $pmidtemp);
13439                                         $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
13440                                         $endlinepos = strlen($pstart."\n".$pmid."\n");
13441                                     } else {
13442                                         // non-unicode (single-byte characters)
13443                                         $rs = sprintf("%.3F Tw", $spacewidth);
13444                                         $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
13445                                         $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
13446                                         $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
13447                                     }
13448                                 }
13449                             } // end of J
13450                             if (($t_x != 0) OR ($yshift < 0)) {
13451                                 // shift the line
13452                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
13453                                 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
13454                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
13455                                 // shift the annotations and links
13456                                 if (isset($this->PageAnnots[$this->page])) {
13457                                     foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
13458                                         if ($pak >= $pask) {
13459                                             $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
13460                                             $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
13461                                         }
13462                                     }
13463                                 }
13464                                 $this->y -= $yshift;
13465                             }
13466                         }
13467                     }
13468                     $this->newline = false;
13469                     $pbrk = $this->checkPageBreak($this->lasth);
13470                     $this->SetFont($fontname, $fontstyle, $fontsize);
13471                     if ($wfill) {
13472                         $this->SetFillColorArray($this->bgcolor);
13473                     }
13474                     $startlinex = $this->x;
13475                     $startliney = $this->y;
13476                     $minstartliney = $this->y;
13477                     $startlinepage = $this->page;
13478                     if (isset($endlinepos) AND (!$pbrk)) {
13479                         $startlinepos = $endlinepos;
13480                         unset($endlinepos);
13481                     } else {
13482                         if (isset($this->footerlen[$this->page])) {
13483                             $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
13484                         } else {
13485                             $this->footerpos[$this->page] = $this->pagelen[$this->page];
13486                         }
13487                         $startlinepos = $this->footerpos[$this->page];
13488                     }
13489                     $plalign = $lalign;
13490                     if (isset($this->PageAnnots[$this->page])) {
13491                         $pask = count($this->PageAnnots[$this->page]);
13492                     } else {
13493                         $pask = 0;
13494                     }
13495                 }
13496                 if (isset($opentagpos)) {
13497                     unset($opentagpos);
13498                 }
13499                 if ($dom[$key]['tag']) {
13500                     if ($dom[$key]['opening']) {
13501                         // get text indentation (if any)
13502                         if (isset($dom[$key]['text-indent']) AND in_array($dom[$key]['value'], array('blockquote','dd','div','dt','h1','h2','h3','h4','h5','h6','li','ol','p','ul','table','tr','td'))) {
13503                             $this->textindent = $dom[$key]['text-indent'];
13504                         }
13505                         if ($dom[$key]['value'] == 'table') {
13506                             if ($this->rtl) {
13507                                 $wtmp = $this->x - $this->lMargin;
13508                             } else {
13509                                 $wtmp = $this->w - $this->rMargin - $this->x;
13510                             }
13511                             $wtmp -= (2 * $this->cMargin);
13512                             // calculate cell width
13513                             if (isset($dom[$key]['width'])) {
13514                                 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
13515                             } else {
13516                                 $table_width = $wtmp;
13517                             }
13518                         }
13519                         // table content is handled in a special way
13520                         if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
13521                             $trid = $dom[$key]['parent'];
13522                             $table_el = $dom[$trid]['parent'];
13523                             if (!isset($dom[$table_el]['cols'])) {
13524                                 $dom[$table_el]['cols'] = $trid['cols'];
13525                             }
13526                             $oldmargin = $this->cMargin;
13527                             if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
13528                                 $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
13529                             } else {
13530                                 $currentcmargin = 0;        
13531                             }
13532                             $this->cMargin = $currentcmargin;
13533                             if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
13534                                 $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px');
13535                             } else {
13536                                 $cellspacing = 0;
13537                             }
13538                             if ($this->rtl) {
13539                                 $cellspacingx = -$cellspacing;
13540                             } else {
13541                                 $cellspacingx = $cellspacing;
13542                             }
13543                             $colspan = $dom[$key]['attribute']['colspan'];
13544                             $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols']));
13545                             if (isset($dom[$key]['width'])) {
13546                                 $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $table_width, 'px');
13547                             } else {
13548                                 $cellw = $wtmp;
13549                             }
13550                             if (isset($dom[$key]['height'])) {
13551                                 // minimum cell height
13552                                 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
13553                             } else {
13554                                 $cellh = 0;
13555                             }
13556                             $cellw -= $cellspacing;
13557                             if (isset($dom[$key]['content'])) {
13558                                 $cell_content = $dom[$key]['content'];
13559                             } else {
13560                                 $cell_content = '&nbsp;';
13561                             }
13562                             $tagtype = $dom[$key]['value'];
13563                             $parentid = $key;
13564                             while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
13565                                 // move $key index forward
13566                                 ++$key;
13567                             }
13568                             if (!isset($dom[$trid]['startpage'])) {
13569                                 $dom[$trid]['startpage'] = $this->page;
13570                             } else {
13571                                 $this->setPage($dom[$trid]['startpage']);
13572                             }
13573                             if (!isset($dom[$trid]['starty'])) {
13574                                 $dom[$trid]['starty'] = $this->y;
13575                             } else {
13576                                 $this->y = $dom[$trid]['starty'];
13577                             }
13578                             if (!isset($dom[$trid]['startx'])) {
13579                                 $dom[$trid]['startx'] = $this->x;
13580                             }
13581                             $this->x += ($cellspacingx / 2);                        
13582                             if (isset($dom[$parentid]['attribute']['rowspan'])) {
13583                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
13584                             } else {
13585                                 $rowspan = 1;
13586                             }
13587                             // skip row-spanned cells started on the previous rows
13588                             if (isset($dom[$table_el]['rowspans'])) {
13589                                 $rsk = 0;
13590                                 $rskmax = count($dom[$table_el]['rowspans']);
13591                                 while ($rsk < $rskmax) {
13592                                     $trwsp = $dom[$table_el]['rowspans'][$rsk];
13593                                     $rsstartx = $trwsp['startx'];
13594                                     $rsendx = $trwsp['endx'];
13595                                     // account for margin changes
13596                                     if ($trwsp['startpage'] < $this->page) {
13597                                         if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
13598                                             $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
13599                                             $rsstartx -= $dl;
13600                                             $rsendx -= $dl;
13601                                         } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
13602                                             $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
13603                                             $rsstartx += $dl;
13604                                             $rsendx += $dl;
13605                                         }
13606                                     }
13607                                     if  (($trwsp['rowspan'] > 0)
13608                                         AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps))
13609                                         AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps))
13610                                         AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) {
13611                                         // set the starting X position of the current cell
13612                                         $this->x = $rsendx + $cellspacingx;
13613                                         if (($trwsp['rowspan'] == 1)
13614                                             AND (isset($dom[$trid]['endy']))
13615                                             AND (isset($dom[$trid]['endpage']))
13616                                             AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) {
13617                                             // set ending Y position for row
13618                                             $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
13619                                             $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
13620                                         }
13621                                         $rsk = 0;
13622                                     } else {
13623                                         ++$rsk;
13624                                     }
13625                                 }
13626                             }
13627                             // add rowspan information to table element
13628                             if ($rowspan > 1) {
13629                                 if (isset($this->footerlen[$this->page])) {
13630                                     $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
13631                                 } else {
13632                                     $this->footerpos[$this->page] = $this->pagelen[$this->page];
13633                                 }
13634                                 $trintmrkpos = $this->footerpos[$this->page];
13635                                 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
13636                             }
13637                             $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
13638                             if ($rowspan > 1) {
13639                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
13640                             }
13641                             // push background colors
13642                             if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
13643                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
13644                             }
13645                             $prevLastH = $this->lasth;
13646                             // ****** write the cell content ******
13647                             $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
13648                             $this->lasth = $prevLastH;
13649                             $this->cMargin = $oldmargin;
13650                             $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
13651                             // update the end of row position
13652                             if ($rowspan <= 1) {
13653                                 if (isset($dom[$trid]['endy'])) {
13654                                     if ($this->page == $dom[$trid]['endpage']) {
13655                                         $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
13656                                     } elseif ($this->page > $dom[$trid]['endpage']) {
13657                                         $dom[$trid]['endy'] = $this->y;
13658                                     }
13659                                 } else {
13660                                     $dom[$trid]['endy'] = $this->y;
13661                                 }
13662                                 if (isset($dom[$trid]['endpage'])) {
13663                                     $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
13664                                 } else {
13665                                     $dom[$trid]['endpage'] = $this->page;
13666                                 }                               
13667                             } else {
13668                                 // account for row-spanned cells
13669                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
13670                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
13671                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
13672                             }
13673                             if (isset($dom[$table_el]['rowspans'])) {
13674                                 // update endy and endpage on rowspanned cells
13675                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
13676                                     if ($trwsp['rowspan'] > 0) {
13677                                         if (isset($dom[$trid]['endpage'])) {
13678                                             if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
13679                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
13680                                             } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) {
13681                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
13682                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
13683                                             } else {
13684                                                 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
13685                                             }
13686                                         }
13687                                     }
13688                                 }
13689                             }
13690                             $this->x += ($cellspacingx / 2);
13691                         } else {
13692                             // opening tag (or self-closing tag)
13693                             if (!isset($opentagpos)) {
13694                                 if (!$this->InFooter) {
13695                                     if (isset($this->footerlen[$this->page])) {
13696                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
13697                                     } else {
13698                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
13699                                     }
13700                                     $opentagpos = $this->footerpos[$this->page];
13701                                 }
13702                             }
13703                             $this->openHTMLTagHandler($dom, $key, $cell);
13704                         }
13705                     } else {
13706                         // closing tag
13707                         $this->closeHTMLTagHandler($dom, $key, $cell);
13708                     }
13709                 } elseif (strlen($dom[$key]['value']) > 0) {
13710                     // print list-item
13711                     if (!$this->empty_string($this->lispacer)) {
13712                         $this->SetFont($pfontname, $pfontstyle, $pfontsize);
13713                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
13714                         $minstartliney = $this->y;
13715                         $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
13716                         $this->SetFont($curfontname, $curfontstyle, $curfontsize);
13717                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
13718                         if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
13719                             $this->y += (($pfontsize - $curfontsize) / $this->k);
13720                             $minstartliney = min($this->y, $minstartliney);
13721                         }
13722                     }
13723                     // text
13724                     $this->htmlvspace = 0;
13725                     if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) {
13726                         // reverse spaces order
13727                         $len1 = strlen($dom[$key]['value']);
13728                         $lsp = $len1 - strlen(ltrim($dom[$key]['value']));
13729                         $rsp = $len1 - strlen(rtrim($dom[$key]['value']));
13730                         $tmpstr = '';
13731                         if ($rsp > 0) {
13732                             $tmpstr .= substr($dom[$key]['value'], -$rsp);
13733                         }
13734                         $tmpstr .= trim($dom[$key]['value']);
13735                         if ($lsp > 0) {
13736                             $tmpstr .= substr($dom[$key]['value'], 0, $lsp);
13737                         }
13738                         $dom[$key]['value'] = $tmpstr;
13739                     }
13740                     if ($newline) {
13741                         if (!$this->premode) {
13742                             if (($this->rtl OR $this->tmprtl)) {
13743                                 $dom[$key]['value'] = rtrim($dom[$key]['value']);
13744                             } else {
13745                                 $dom[$key]['value'] = ltrim($dom[$key]['value']);
13746                             }
13747                         }
13748                         $newline = false;
13749                         $firstblock = true;
13750                     } else {
13751                         $firstblock = false;
13752                     }
13753                     $strrest = '';
13754                     if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
13755                         // HTML <a> Link
13756                         $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']);
13757                     } else {
13758                         $ctmpmargin = $this->cMargin;
13759                         $this->cMargin = 0;
13760                         if ($this->rtl) {
13761                             $this->x -= $this->textindent;
13762                         } else {
13763                             $this->x += $this->textindent;
13764                         }
13765                         // ****** write only until the end of the line and get the rest ******
13766                         $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock);
13767                         $this->textindent = 0;
13768                         $this->cMargin = $ctmpmargin;
13769                     }
13770                     if (strlen($strrest) > 0) {
13771                         // store the remaining string on the previous $key position
13772                         $this->newline = true;
13773                         if ($cell) {
13774                             if ($this->rtl) {
13775                                 $this->x -= $this->cMargin;
13776                             } else {
13777                                 $this->x += $this->cMargin;
13778                             }
13779                         }
13780                         if ($strrest == $dom[$key]['value']) {
13781                             // used to avoid infinite loop
13782                             ++$loop;
13783                         } else {
13784                             $loop = 0;
13785                         }
13786                         $dom[$key]['value'] = ltrim($strrest);
13787                         if ($loop < 3) {
13788                             --$key;
13789                         }
13790                     } else {
13791                         $loop = 0;
13792                     }
13793                 }
13794                 ++$key;
13795                 if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
13796                     if ((!$undo) AND ($this->start_transaction_page == ($this->numpages - 1))) {
13797                         // restore previous object
13798                         $this->rollbackTransaction(true);
13799                         // restore previous values
13800                         foreach ($this_method_vars as $vkey => $vval) {
13801                             $$vkey = $vval;
13802                         }
13803                         // add a page
13804                         $this->AddPage();
13805                         $undo = true; // avoid infinite loop
13806                     } else {
13807                         $undo = false;
13808                     }
13809                 }
13810             } // end for each $key
13811             // align the last line
13812             if (isset($startlinex)) {
13813                 $yshift = $minstartliney - $startliney;
13814                 if (($yshift > 0) OR ($this->page > $startlinepage)) {
13815                     $yshift = 0;
13816                 }
13817                 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
13818                     // the last line must be shifted to be aligned as requested
13819                     $linew = abs($this->endlinex - $startlinex);
13820                     $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
13821                     if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
13822                         $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
13823                         $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
13824                     } elseif (isset($opentagpos)) {
13825                         $midpos = $opentagpos;
13826                     } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
13827                         $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
13828                         $midpos = $this->footerpos[$startlinepage];
13829                     } else {
13830                         $midpos = 0;
13831                     }
13832                     if ($midpos > 0) {
13833                         $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
13834                         $pend = substr($this->getPageBuffer($startlinepage), $midpos);
13835                     } else {
13836                         $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
13837                         $pend = '';
13838                     }   
13839                     // calculate shifting amount
13840                     $tw = $w;
13841                     if ($this->lMargin != $prevlMargin) {
13842                         $tw += ($prevlMargin - $this->lMargin);
13843                     }
13844                     if ($this->rMargin != $prevrMargin) {
13845                         $tw += ($prevrMargin - $this->rMargin);
13846                     }
13847                     $mdiff = abs($tw - $linew);
13848                     if ($plalign == 'C') {
13849                         if ($this->rtl) {
13850                             $t_x = -($mdiff / 2);
13851                         } else {
13852                             $t_x = ($mdiff / 2);
13853                         }
13854                     } elseif (($plalign == 'R') AND (!$this->rtl)) {
13855                         // right alignment on LTR document
13856                         $t_x = $mdiff;
13857                     } elseif (($plalign == 'L') AND ($this->rtl)) {
13858                         // left alignment on RTL document
13859                         $t_x = -$mdiff;
13860                     } else {
13861                         $t_x = 0;
13862                     }
13863                     if (($t_x != 0) OR ($yshift < 0)) {
13864                         // shift the line
13865                         $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
13866                         $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
13867                         $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
13868                         // shift the annotations and links
13869                         if (isset($this->PageAnnots[$this->page])) {
13870                             foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
13871                                 if ($pak >= $pask) {
13872                                     $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
13873                                     $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
13874                                 }
13875                             }
13876                         }
13877                         $this->y -= $yshift;
13878                     }
13879                 }
13880             }
13881             if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
13882                 $this->Ln($this->lasth);
13883             }
13884             // restore previous values
13885             $this->setGraphicVars($gvars);
13886             if ($this->page > $prevPage) {
13887                 $this->lMargin = $this->pagedim[$this->page]['olm'];
13888                 $this->rMargin = $this->pagedim[$this->page]['orm'];
13889             }
13890             // restore previous list state
13891             $this->listnum = $prev_listnum;
13892             $this->listordered = $prev_listordered;
13893             $this->listcount = $prev_listcount;
13894             $this->lispacer = $prev_lispacer;
13895             unset($dom);
13896         }
13897         
13905         protected function openHTMLTagHandler(&$dom, $key, $cell=false) {
13906             $tag = $dom[$key];
13907             $parent = $dom[($dom[$key]['parent'])];
13908             $firstorlast = ($key == 1);
13909             // check for text direction attribute
13910             if (isset($tag['attribute']['dir'])) {
13911                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
13912             } else {
13913                 $this->tmprtl = false;
13914             }
13915             //Opening tag
13916             switch($tag['value']) {
13917                 case 'table': {
13918                     $cp = 0;
13919                     $cs = 0;
13920                     $dom[$key]['rowspans'] = array();
13921                     if (!$this->empty_string($dom[$key]['thead'])) {
13922                         // set table header
13923                         $this->thead = $dom[$key]['thead'];
13924                         if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
13925                             $this->theadMargins = array();
13926                             $this->theadMargins['cmargin'] = $this->cMargin;
13927                         }
13928                     }
13929                     if (isset($tag['attribute']['cellpadding'])) {
13930                         $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
13931                         $this->oldcMargin = $this->cMargin;
13932                         $this->cMargin = $cp;
13933                     }
13934                     if (isset($tag['attribute']['cellspacing'])) {
13935                         $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
13936                     }
13937                     $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth);
13938                     break;
13939                 }
13940                 case 'tr': {
13941                     // array of columns positions
13942                     $dom[$key]['cellpos'] = array();
13943                     break;
13944                 }
13945                 case 'hr': {
13946                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
13947                     $this->htmlvspace = 0;
13948                     $wtmp = $this->w - $this->lMargin - $this->rMargin;
13949                     if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
13950                         $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px');
13951                     } else {
13952                         $hrWidth = $wtmp;
13953                     }
13954                     $x = $this->GetX();
13955                     $y = $this->GetY();
13956                     $prevlinewidth = $this->GetLineWidth();
13957                     $this->Line($x, $y, $x + $hrWidth, $y);
13958                     $this->SetLineWidth($prevlinewidth);
13959                     $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false);
13960                     break;
13961                 }
13962                 case 'a': {
13963                     if (array_key_exists('href', $tag['attribute'])) {
13964                         $this->HREF['url'] = $tag['attribute']['href'];
13965                     }
13966                     $this->HREF['color'] = $this->htmlLinkColorArray;
13967                     $this->HREF['style'] = $this->htmlLinkFontStyle;
13968                     if (array_key_exists('style', $tag['attribute'])) {
13969                         // get style attributes
13970                         preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
13971                         $astyle = array();
13972                         while (list($id, $name) = each($style_array[1])) {
13973                             $name = strtolower($name);
13974                             $astyle[$name] = trim($style_array[2][$id]);
13975                         }
13976                         if (isset($astyle['color'])) {
13977                             $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']);
13978                         }
13979                         if (isset($astyle['text-decoration'])) {
13980                             $this->HREF['style'] = '';
13981                             $decors = explode(' ', strtolower($astyle['text-decoration']));
13982                             foreach ($decors as $dec) {
13983                                 $dec = trim($dec);
13984                                 if (!$this->empty_string($dec)) {
13985                                     if ($dec{0} == 'u') {
13986                                         $this->HREF['style'] .= 'U';
13987                                     } elseif ($dec{0} == 'l') {
13988                                         $this->HREF['style'] .= 'D';
13989                                     }
13990                                 }
13991                             }
13992                         }
13993                     }       
13994                     break;
13995                 }
13996                 case 'img': {
13997                     if (isset($tag['attribute']['src'])) {
13998                         // replace relative path with real server path
13999                         if (($tag['attribute']['src'][0] == '/') AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
14000                             $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
14001                         }
14002                         $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
14003                         $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
14004                         if (!isset($tag['attribute']['width'])) {
14005                             $tag['attribute']['width'] = 0;
14006                         }
14007                         if (!isset($tag['attribute']['height'])) {
14008                             $tag['attribute']['height'] = 0;
14009                         }
14010                         //if (!isset($tag['attribute']['align'])) {
14011                             // the only alignment supported is "bottom"
14012                             // further development is required for other modes.
14013                             $tag['attribute']['align'] = 'bottom';
14014                         //} 
14015                         switch($tag['attribute']['align']) {
14016                             case 'top': {
14017                                 $align = 'T';
14018                                 break;
14019                             }
14020                             case 'middle': {
14021                                 $align = 'M';
14022                                 break;
14023                             }
14024                             case 'bottom': {
14025                                 $align = 'B';
14026                                 break;
14027                             }
14028                             default: {
14029                                 $align = 'B';
14030                                 break;
14031                             }
14032                         }
14033                         $fileinfo = pathinfo($tag['attribute']['src']);
14034                         if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
14035                             $type = strtolower($fileinfo['extension']);
14036                         }
14037                         $prevy = $this->y;
14038                         $xpos = $this->GetX();
14039                         if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) {
14040                             if ($this->rtl) {
14041                                 $xpos += $this->GetStringWidth(' ');
14042                             } else {
14043                                 $xpos -= $this->GetStringWidth(' ');
14044                             }
14045                         }
14046                         $imglink = '';
14047                         if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
14048                             $imglink = $this->HREF['url'];
14049                             if ($imglink{0} == '#') {
14050                                 // convert url to internal link
14051                                 $page = intval(substr($imglink, 1));
14052                                 $imglink = $this->AddLink();
14053                                 $this->SetLink($imglink, 0, $page);
14054                             }
14055                         }
14056                         $border = 0;
14057                         if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) {
14058                             // currently only support 1 (frame) or a combination of 'LTRB'
14059                             $border = $tag['attribute']['border'];
14060                         }
14061                         $iw = '';
14062                         if (isset($tag['attribute']['width'])) {
14063                             $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false);
14064                         }
14065                         $ih = '';
14066                         if (isset($tag['attribute']['height'])) {
14067                             $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false);
14068                         }
14069                         if (($type == 'eps') OR ($type == 'ai')) {
14070                             $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border);
14071                         } else {
14072                             $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border);
14073                         }
14074                         switch($align) {
14075                             case 'T': {
14076                                 $this->y = $prevy;
14077                                 break;
14078                             }
14079                             case 'M': {
14080                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
14081                                 break;
14082                             }
14083                             case 'B': {
14084                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
14085                                 break;
14086                             }
14087                         }
14088                     }
14089                     break;
14090                 }
14091                 case 'dl': {
14092                     ++$this->listnum;
14093                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
14094                     break;
14095                 }
14096                 case 'dt': {
14097                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
14098                     break;
14099                 }
14100                 case 'dd': {
14101                     if ($this->rtl) {
14102                         $this->rMargin += $this->listindent;
14103                     } else {
14104                         $this->lMargin += $this->listindent;
14105                     }
14106                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
14107                     break;
14108                 }
14109                 case 'ul':
14110                 case 'ol': {
14111                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
14112                     $this->htmlvspace = 0;
14113                     ++$this->listnum;
14114                     if ($tag['value'] == 'ol') {
14115                         $this->listordered[$this->listnum] = true;
14116                     } else {
14117                         $this->listordered[$this->listnum] = false;
14118                     }
14119                     if (isset($tag['attribute']['start'])) {
14120                         $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
14121                     } else {
14122                         $this->listcount[$this->listnum] = 0;
14123                     }
14124                     if ($this->rtl) {
14125                         $this->rMargin += $this->listindent;
14126                     } else {
14127                         $this->lMargin += $this->listindent;
14128                     }
14129                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
14130                     $this->htmlvspace = 0;
14131                     break;
14132                 }
14133                 case 'li': {
14134                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
14135                     if ($this->listordered[$this->listnum]) {
14136                         // ordered item
14137                         if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
14138                             $this->lispacer = $parent['attribute']['type'];
14139                         } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
14140                             $this->lispacer = $parent['listtype'];
14141                         } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
14142                             $this->lispacer = $this->lisymbol;
14143                         } else {
14144                             $this->lispacer = '#';
14145                         }
14146                         ++$this->listcount[$this->listnum];
14147                         if (isset($tag['attribute']['value'])) {
14148                             $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
14149                         }
14150                     } else {
14151                         // unordered item
14152                         if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
14153                             $this->lispacer = $parent['attribute']['type'];
14154                         } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
14155                             $this->lispacer = $parent['listtype'];
14156                         } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
14157                             $this->lispacer = $this->lisymbol;
14158                         } else {
14159                             $this->lispacer = '!';
14160                         }
14161                     }
14162                     break;
14163                 }
14164                 case 'blockquote': {
14165                     if ($this->rtl) {
14166                         $this->rMargin += $this->listindent;
14167                     } else {
14168                         $this->lMargin += $this->listindent;
14169                     }
14170                     $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
14171                     break;
14172                 }
14173                 case 'br': {
14174                     $this->Ln('', $cell);
14175                     break;
14176                 }
14177                 case 'div': {
14178                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
14179                     break;
14180                 }
14181                 case 'p': {
14182                     $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
14183                     break;
14184                 }
14185                 case 'pre': {
14186                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
14187                     $this->premode = true;
14188                     break;
14189                 }
14190                 case 'sup': {
14191                     $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
14192                     break;
14193                 }
14194                 case 'sub': {
14195                     $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
14196                     break;
14197                 }
14198                 case 'h1': 
14199                 case 'h2': 
14200                 case 'h3': 
14201                 case 'h4': 
14202                 case 'h5': 
14203                 case 'h6': {
14204                     $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false);
14205                     break;
14206                 }
14207                 // Form fields (since 4.8.000 - 2009-09-07)
14208                 case 'form': {
14209                     if (isset($tag['attribute']['action'])) {
14210                         $this->form_action = $tag['attribute']['action'];
14211                     } else {
14212                         $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME'];
14213                     }
14214                     if (isset($tag['attribute']['enctype'])) {
14215                         $this->form_enctype = $tag['attribute']['enctype'];
14216                     } else {
14217                         $this->form_enctype = 'application/x-www-form-urlencoded';
14218                     }
14219                     if (isset($tag['attribute']['method'])) {
14220                         $this->form_mode = $tag['attribute']['method'];
14221                     } else {
14222                         $this->form_mode = 'post';
14223                     }
14224                     break;
14225                 }
14226                 case 'input': {
14227                     if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
14228                         $name = $tag['attribute']['name'];
14229                     } else {
14230                         break;
14231                     }
14232                     $prop = array();
14233                     $opt = array();
14234                     if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
14235                         $value = $tag['attribute']['value'];
14236                     }
14237                     if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) {
14238                         $opt['maxlen'] = intval($tag['attribute']['value']);
14239                     }
14240                     $h = $this->FontSize * $this->cell_height_ratio;
14241                     if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
14242                         $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
14243                     } else {
14244                         $w = $h;
14245                     }
14246                     if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
14247                         $checked = true;
14248                     } else {
14249                         $checked = false;
14250                     }
14251                     switch ($tag['attribute']['type']) {
14252                         case 'text': {
14253                             if (isset($value)) {
14254                                 $opt['v'] = $value;
14255                             }
14256                             $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
14257                             break;
14258                         }
14259                         case 'password': {
14260                             if (isset($value)) {
14261                                 $opt['v'] = $value;
14262                             }
14263                             $prop['password'] = 'true';
14264                             $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
14265                             break;
14266                         }
14267                         case 'checkbox': {
14268                             $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
14269                             break;
14270                         }
14271                         case 'radio': {
14272                             $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
14273                             break;
14274                         }
14275                         case 'submit': {
14276                             $w = $this->GetStringWidth($value) * 1.5;
14277                             $h *= 1.6;
14278                             $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
14279                             $action = array();
14280                             $action['S'] = 'SubmitForm';
14281                             $action['F'] = $this->form_action;
14282                             if ($this->form_enctype != 'FDF') {
14283                                 $action['Flags'] = array('ExportFormat');
14284                             }
14285                             if ($this->form_mode == 'get') {
14286                                 $action['Flags'] = array('GetMethod');
14287                             }
14288                             $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
14289                             break;
14290                         }
14291                         case 'reset': {
14292                             $w = $this->GetStringWidth($value) * 1.5;
14293                             $h *= 1.6;
14294                             $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
14295                             $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
14296                             break;
14297                         }
14298                         case 'file': {
14299                             $prop['fileSelect'] = 'true';
14300                             $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
14301                             if (!isset($value)) {
14302                                 $value = '*';
14303                             }
14304                             $w = $this->GetStringWidth($value) * 2;
14305                             $h *= 1.2;
14306                             $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
14307                             $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
14308                             $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
14309                             break;
14310                         }
14311                         case 'hidden': {
14312                             if (isset($value)) {
14313                                 $opt['v'] = $value;
14314                             }
14315                             $opt['f'] = array('invisible', 'hidden');
14316                             $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
14317                             break;
14318                         }
14319                         case 'image': {
14320                             // THIS TYPE MUST BE FIXED
14321                             if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) {
14322                                 $img = $tag['attribute']['src'];
14323                             } else {
14324                                 break;
14325                             }
14326                             $value = 'img';
14327                             //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
14328                             if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
14329                                 $jsaction = $tag['attribute']['onclick'];
14330                             } else {
14331                                 $jsaction = '';
14332                             }
14333                             $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
14334                             break;
14335                         }
14336                         case 'button': {
14337                             $w = $this->GetStringWidth($value) * 1.5;
14338                             $h *= 1.6;
14339                             $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
14340                             if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
14341                                 $jsaction = $tag['attribute']['onclick'];
14342                             } else {
14343                                 $jsaction = '';
14344                             }
14345                             $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
14346                             break;
14347                         }
14348                     }
14349                     break;
14350                 }
14351                 case 'textarea': {
14352                     $prop = array();
14353                     $opt = array();
14354                     if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
14355                         $name = $tag['attribute']['name'];
14356                     } else {
14357                         break;
14358                     }
14359                     if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
14360                         $opt['v'] = $tag['attribute']['value'];
14361                     }
14362                     if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) {
14363                         $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
14364                     } else {
14365                         $w = 40;
14366                     }
14367                     if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) {
14368                         $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio;
14369                     } else {
14370                         $h = 10;
14371                     }
14372                     $prop['multiline'] = 'true';
14373                     $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
14374                     break;
14375                 }
14376                 case 'select': {
14377                     $h = $this->FontSize * $this->cell_height_ratio;
14378                     if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
14379                         $h *= ($tag['attribute']['size'] + 1);
14380                     }
14381                     $prop = array();
14382                     $opt = array();
14383                     if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
14384                         $name = $tag['attribute']['name'];
14385                     } else {
14386                         break;
14387                     }
14388                     $w = 0;
14389                     if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) {
14390                         $options = explode ("\r", $tag['attribute']['opt']);
14391                         $values = array();
14392                         foreach ($options as $val) {
14393                             if (strpos($val, "\t") !== false) {
14394                                 $opts = explode("\t", $val);
14395                                 $values[] = $opts;
14396                                 $w = max($w, $this->GetStringWidth($opts[1]));
14397                             } else {
14398                                 $values[] = $val;
14399                                 $w = max($w, $this->GetStringWidth($val));
14400                             }
14401                         }
14402                     } else {
14403                         break;
14404                     }
14405                     $w *= 2;
14406                     if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
14407                         $prop['multipleSelection'] = 'true';
14408                         $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
14409                     } else {
14410                         $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
14411                     }
14412                     break;
14413                 }
14414                 case 'tcpdf': {
14415                     // NOT HTML: used to call TCPDF methods
14416                     if (isset($tag['attribute']['method'])) {
14417                         $tcpdf_method = $tag['attribute']['method'];
14418                         if (method_exists($this, $tcpdf_method)) {
14419                             if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
14420                                 eval('$params = array('.$this->unhtmlentities($tag['attribute']['params']).');');
14421                                 call_user_func_array(array($this, $tcpdf_method), $params);
14422                             } else {
14423                                 $this->$tcpdf_method();
14424                             }
14425                             $this->newline = true;
14426                         }
14427                     }
14428                 }
14429                 default: {
14430                     break;
14431                 }
14432             }
14433         }
14434         
14442         protected function closeHTMLTagHandler(&$dom, $key, $cell=false) {
14443             $tag = $dom[$key];
14444             $parent = $dom[($dom[$key]['parent'])];
14445             $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
14446             $in_table_head = false;
14447             //Closing tag
14448             switch($tag['value']) {
14449                 case 'tr': {
14450                     $table_el = $dom[($dom[$key]['parent'])]['parent'];
14451                     if(!isset($parent['endy'])) {
14452                         $dom[($dom[$key]['parent'])]['endy'] = $this->y;
14453                         $parent['endy'] = $this->y;
14454                     }
14455                     if(!isset($parent['endpage'])) {
14456                         $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
14457                         $parent['endpage'] = $this->page;
14458                     }
14459                     // update row-spanned cells
14460                     if (isset($dom[$table_el]['rowspans'])) {
14461                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
14462                             $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
14463                             if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
14464                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) {
14465                                     $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
14466                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) {
14467                                     $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
14468                                     $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
14469                                 }
14470                             }
14471                         }
14472                         // report new endy and endpage to the rowspanned cells
14473                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
14474                             if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
14475                                 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
14476                                 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
14477                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
14478                                 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
14479                             }
14480                         }
14481                         // update remaining rowspanned cells
14482                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
14483                             if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
14484                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
14485                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
14486                             }
14487                         }
14488                     }
14489                     $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
14490                     $this->y = $dom[($dom[$key]['parent'])]['endy'];
14491                     if (isset($dom[$table_el]['attribute']['cellspacing'])) {
14492                         $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
14493                         $this->y += $cellspacing;
14494                     }               
14495                     $this->Ln(0, $cell);
14496                     $this->x = $parent['startx'];
14497                     // account for booklet mode
14498                     if ($this->page > $parent['startpage']) {
14499                         if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
14500                             $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
14501                         } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
14502                             $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
14503                         }
14504                     }
14505                     break;
14506                 }
14507                 case 'tablehead':
14508                     // closing tag used for the thead part
14509                     $in_table_head = true;
14510                 case 'table': {
14511                     // draw borders
14512                     $table_el = $parent;
14513                     if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) 
14514                         OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
14515                             $border = 1;
14516                     } else {
14517                         $border = 0;
14518                     }
14519                     // fix bottom line alignment of last line before page break
14520                     foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
14521                         // update row-spanned cells
14522                         if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
14523                             foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
14524                                 if ($trwsp['trid'] == $trkey) {
14525                                     $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
14526                                 }
14527                                 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
14528                                     $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
14529                                 }
14530                             }
14531                         }
14532                         if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
14533                             $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
14534                             $dom[$prevtrkey]['endy'] = $pgendy;
14535                             // update row-spanned cells
14536                             if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
14537                                 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
14538                                     if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
14539                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
14540                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
14541                                     }
14542                                 }
14543                             }
14544                         }
14545                         $prevtrkey = $trkey;
14546                         $table_el = $dom[($dom[$key]['parent'])];
14547                     }
14548                     // for each row
14549                     foreach ($table_el['trids'] as $j => $trkey) {
14550                         $parent = $dom[$trkey];
14551                         // for each cell on the row
14552                         foreach ($parent['cellpos'] as $k => $cellpos) {
14553                             if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
14554                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
14555                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
14556                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
14557                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
14558                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
14559                             } else {
14560                                 $endy = $parent['endy'];
14561                                 $startpage = $parent['startpage'];
14562                                 $endpage = $parent['endpage'];
14563                             }
14564                             if ($endpage > $startpage) {
14565                                 // design borders around HTML cells.
14566                                 for ($page=$startpage; $page <= $endpage; ++$page) {
14567                                     $this->setPage($page);
14568                                     if ($page == $startpage) {
14569                                         $this->y = $parent['starty']; // put cursor at the beginning of row on the first page
14570                                         $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin();
14571                                         $cborder = $this->getBorderMode($border, $position='start');
14572                                     } elseif ($page == $endpage) {
14573                                         $this->y = $this->tMargin; // put cursor at the beginning of last page
14574                                         $ch = $endy - $this->tMargin;
14575                                         $cborder = $this->getBorderMode($border, $position='end');
14576                                     } else {
14577                                         $this->y = $this->tMargin; // put cursor at the beginning of the current page
14578                                         $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
14579                                         $cborder = $this->getBorderMode($border, $position='middle');
14580                                     }
14581                                     if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
14582                                         $this->SetFillColorArray($cellpos['bgcolor']);
14583                                         $fill = true;
14584                                     } else {
14585                                         $fill = false;
14586                                     }
14587                                     $cw = abs($cellpos['endx'] - $cellpos['startx']);
14588                                     $this->x = $cellpos['startx'];
14589                                     // account for margin changes
14590                                     if ($page > $startpage) {
14591                                         if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
14592                                             $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
14593                                         } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) {
14594                                             $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
14595                                         }
14596                                     }
14597                                     // design a cell around the text
14598                                     $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true);
14599                                     if ($cborder OR $fill) {
14600                                         $pagebuff = $this->getPageBuffer($this->page);
14601                                         $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
14602                                         $pend = substr($pagebuff, $this->intmrk[$this->page]);
14603                                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
14604                                         $this->intmrk[$this->page] += strlen($ccode."\n");
14605                                     }
14606                                 }
14607                             } else {
14608                                 $this->setPage($startpage);
14609                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
14610                                     $this->SetFillColorArray($cellpos['bgcolor']);
14611                                     $fill = true;
14612                                 } else {
14613                                     $fill = false;
14614                                 }
14615                                 $this->x = $cellpos['startx'];
14616                                 $this->y = $parent['starty'];
14617                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
14618                                 $ch = $endy - $parent['starty'];
14619                                 // design a cell around the text
14620                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true);
14621                                 if ($border OR $fill) {
14622                                     if (end($this->transfmrk[$this->page]) !== false) {
14623                                         $pagemarkkey = key($this->transfmrk[$this->page]);
14624                                         $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
14625                                     } elseif ($this->InFooter) {
14626                                         $pagemark = &$this->footerpos[$this->page];
14627                                     } else {
14628                                         $pagemark = &$this->intmrk[$this->page];
14629                                     }
14630                                     $pagebuff = $this->getPageBuffer($this->page);
14631                                     $pstart = substr($pagebuff, 0, $pagemark);
14632                                     $pend = substr($pagebuff, $pagemark);
14633                                     $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
14634                                     $pagemark += strlen($ccode."\n");
14635                                 }                   
14636                             }
14637                         }                   
14638                         if (isset($table_el['attribute']['cellspacing'])) {
14639                             $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
14640                             $this->y += $cellspacing;
14641                         }               
14642                         $this->Ln(0, $cell);
14643                         $this->x = $parent['startx'];
14644                         if ($endpage > $startpage) {
14645                             if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
14646                                 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
14647                             } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
14648                                 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
14649                             }
14650                         }
14651                     }
14652                     if (!$in_table_head) {
14653                         // we are not inside a thead section
14654                         if (isset($parent['cellpadding'])) {
14655                             $this->cMargin = $this->oldcMargin;
14656                         }
14657                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
14658                         if (isset($this->theadMargins['top'])) {
14659                             // restore top margin
14660                             $this->tMargin = $this->theadMargins['top'];
14661                             $this->pagedim[$this->page]['tm'] = $this->tMargin;
14662                         }
14663                         // reset table header
14664                         $this->thead = '';
14665                         $this->theadMargins = array();
14666                     }
14667                     break;
14668                 }
14669                 case 'a': {
14670                     $this->HREF = '';
14671                     break;
14672                 }
14673                 case 'sup': {
14674                     $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
14675                     break;
14676                 }
14677                 case 'sub': {
14678                     $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
14679                     break;
14680                 }
14681                 case 'div': {
14682                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
14683                     break;
14684                 }
14685                 case 'blockquote': {
14686                     if ($this->rtl) {
14687                         $this->rMargin -= $this->listindent;
14688                     } else {
14689                         $this->lMargin -= $this->listindent;
14690                     }
14691                     $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
14692                     break;
14693                 }
14694                 case 'p': {
14695                     $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
14696                     break;
14697                 }
14698                 case 'pre': {
14699                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
14700                     $this->premode = false;
14701                     break;
14702                 }
14703                 case 'dl': {
14704                     --$this->listnum;
14705                     if ($this->listnum <= 0) {
14706                         $this->listnum = 0;
14707                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
14708                     }
14709                     break;
14710                 }
14711                 case 'dt': {
14712                     $this->lispacer = '';
14713                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
14714                     break;
14715                 }
14716                 case 'dd': {
14717                     $this->lispacer = '';
14718                     if ($this->rtl) {
14719                         $this->rMargin -= $this->listindent;
14720                     } else {
14721                         $this->lMargin -= $this->listindent;
14722                     }
14723                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
14724                     break;
14725                 }
14726                 case 'ul':
14727                 case 'ol': {
14728                     --$this->listnum;
14729                     $this->lispacer = '';
14730                     if ($this->rtl) {
14731                         $this->rMargin -= $this->listindent;
14732                     } else {
14733                         $this->lMargin -= $this->listindent;
14734                     }
14735                     if ($this->listnum <= 0) {
14736                         $this->listnum = 0;
14737                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
14738                     }
14739                     $this->lasth = $this->FontSize * $this->cell_height_ratio;
14740                     break;
14741                 }
14742                 case 'li': {
14743                     $this->lispacer = '';
14744                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
14745                     break;
14746                 }
14747                 case 'h1': 
14748                 case 'h2': 
14749                 case 'h3': 
14750                 case 'h4': 
14751                 case 'h5': 
14752                 case 'h6': {
14753                     $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true);
14754                     break;
14755                 }
14756                 // Form fields (since 4.8.000 - 2009-09-07)
14757                 case 'form': {
14758                     $this->form_action = '';
14759                     $this->form_enctype = 'application/x-www-form-urlencoded';
14760                     break;
14761                 }
14762                 default : {
14763                     break;
14764                 }
14765             }
14766             $this->tmprtl = false;
14767         }
14768         
14779         protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) {
14780             if ($firstorlast) {
14781                 $this->Ln(0, $cell);
14782                 $this->htmlvspace = 0;
14783                 return;
14784             }
14785             if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) {
14786                 $n = $this->tagvspaces[$tag][intval($closing)]['n'];
14787             }
14788             if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) {
14789                 $h = $this->tagvspaces[$tag][intval($closing)]['h'];
14790             }
14791             if (is_string($h)) {
14792                 $vsize = $n * $this->lasth;
14793             } else {
14794                 $vsize = $n * $h;
14795             }
14796             if ($vsize > $this->htmlvspace) {
14797                 $this->Ln(($vsize - $this->htmlvspace), $cell);
14798                 $this->htmlvspace = $vsize;
14799             }
14800         }
14801         
14808         public function setLIsymbol($symbol='!') {
14809             $symbol = strtolower($symbol);
14810             switch ($symbol) {
14811                 case '!' :
14812                 case '#' :
14813                 case 'disc' :
14814                 case 'disc' :
14815                 case 'circle' :
14816                 case 'square' :
14817                 case '1':
14818                 case 'decimal':
14819                 case 'decimal-leading-zero':
14820                 case 'i':
14821                 case 'lower-roman':
14822                 case 'I':
14823                 case 'upper-roman':
14824                 case 'a':
14825                 case 'lower-alpha':
14826                 case 'lower-latin':
14827                 case 'A':
14828                 case 'upper-alpha':
14829                 case 'upper-latin':
14830                 case 'lower-greek': {
14831                     $this->lisymbol = $symbol;
14832                     break;
14833                 }
14834                 default : {
14835                     $this->lisymbol = '';
14836                 }
14837             }
14838         }
14839         
14848         public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
14849             $this->booklet = $booklet;
14850             if ($inner >= 0) {
14851                 $this->lMargin = $inner;
14852             }
14853             if ($outer >= 0) {
14854                 $this->rMargin = $outer;
14855             }
14856         }
14857         
14864         protected function swapMargins($reverse=true) {
14865             if ($reverse) {
14866                 // swap left and right margins
14867                 $mtemp = $this->original_lMargin;
14868                 $this->original_lMargin = $this->original_rMargin;
14869                 $this->original_rMargin = $mtemp;
14870                 $deltam = $this->original_lMargin - $this->original_rMargin;
14871                 $this->lMargin += $deltam;
14872                 $this->rMargin -= $deltam;
14873             }
14874         }
14875 
14888         public function setHtmlVSpace($tagvs) {
14889             $this->tagvspaces = $tagvs;
14890         }
14891 
14898         public function setListIndentWidth($width) {
14899             return $this->customlistindent = floatval($width);
14900         }
14901 
14908         public function setOpenCell($isopen) {
14909             $this->opencell = $isopen;
14910         }
14911 
14919         public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
14920             $this->htmlLinkColorArray = $color;
14921             $this->htmlLinkFontStyle = $fontstyle;
14922         }
14923 
14934         public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
14935             $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
14936             $retval = 0;
14937             $value = 0;
14938             $unit = 'px';
14939             $k = $this->k;
14940             if ($points) {
14941                 $k = 1;
14942             }
14943             if (in_array($defaultunit, $supportedunits)) {
14944                 $unit = $defaultunit;
14945             }
14946             if (is_numeric($htmlval)) {
14947                 $value = floatval($htmlval);
14948             } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) {
14949                 $value = floatval($mnum[1]);
14950                 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
14951                     if (in_array($munit[1], $supportedunits)) {
14952                         $unit = $munit[1];
14953                     }
14954                 }
14955             }
14956             switch ($unit) {
14957                 // percentage
14958                 case '%': {
14959                     $retval = (($value * $refsize) / 100);
14960                     break;
14961                 }
14962                 // relative-size
14963                 case 'em': {
14964                     $retval = ($value * $refsize);
14965                     break;
14966                 }
14967                 case 'ex': {
14968                     $retval = $value * ($refsize / 2);
14969                     break;
14970                 }
14971                 // absolute-size
14972                 case 'in': {
14973                     $retval = ($value * $this->dpi) / $k;
14974                     break;
14975                 }
14976                 case 'cm': {
14977                     $retval = ($value / 2.54 * $this->dpi) / $k;
14978                     break;
14979                 }
14980                 case 'mm': {
14981                     $retval = ($value / 25.4 * $this->dpi) / $k;
14982                     break;
14983                 }
14984                 case 'pc': {
14985                     // one pica is 12 points
14986                     $retval = ($value * 12) / $k;
14987                     break;
14988                 }
14989                 case 'pt': {
14990                     $retval = $value / $k;
14991                     break;
14992                 }
14993                 case 'px': {
14994                     $retval = $this->pixelsToUnits($value);
14995                     break;
14996                 }
14997             }
14998             return $retval;
14999         }
15000 
15008         public function intToRoman($number) {
15009             $roman = '';
15010             while ($number >= 1000) {
15011                 $roman .= 'M';
15012                 $number -= 1000;
15013             }
15014             while ($number >= 900) {
15015                 $roman .= 'CM';
15016                 $number -= 900;
15017             }
15018             while ($number >= 500) {
15019                 $roman .= 'D';
15020                 $number -= 500;
15021             }
15022             while ($number >= 400) {
15023                 $roman .= 'CD';
15024                 $number -= 400;
15025             }
15026             while ($number >= 100) {
15027                 $roman .= 'C';
15028                 $number -= 100;
15029             }
15030             while ($number >= 90) {
15031             $roman .= 'XC';
15032             $number -= 90;
15033             }
15034             while ($number >= 50) {
15035                 $roman .= 'L';
15036                 $number -= 50;
15037             }
15038             while ($number >= 40) {
15039                 $roman .= 'XL';
15040                 $number -= 40;
15041             }
15042             while ($number >= 10) {
15043             $roman .= 'X';
15044             $number -= 10;
15045             }
15046             while ($number >= 9) {
15047                 $roman .= 'IX';
15048                 $number -= 9;
15049             }
15050             while ($number >= 5) {
15051                 $roman .= 'V';
15052                 $number -= 5;
15053             }
15054             while ($number >= 4) {
15055             $roman .= 'IV';
15056             $number -= 4;
15057             }
15058             while ($number >= 1) {
15059                 $roman .= 'I';
15060                 --$number;
15061             }
15062             return $roman;
15063         }
15064 
15073         protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
15074             $size /= $this->k;
15075             $fill = '';
15076             $color = $this->fgcolor;
15077             $width = 0;
15078             $textitem = '';
15079             $tmpx = $this->x;       
15080             $lspace = $this->GetStringWidth('  ');
15081             if ($listtype == '!') {
15082                 // set default list type for unordered list
15083                 $deftypes = array('disc', 'circle', 'square');
15084                 $listtype = $deftypes[($listdepth - 1) % 3];
15085             } elseif ($listtype == '#') {
15086                 // set default list type for ordered list
15087                 $listtype = 'decimal';
15088             }
15089             switch ($listtype) {
15090                 // unordered types
15091                 case 'none': {
15092                     break;
15093                 }
15094                 case 'disc': {
15095                     $fill = 'F';
15096                 }
15097                 case 'circle': {
15098                     $fill .= 'D';
15099                     $r = $size / 6;
15100                     $lspace += (2 * $r);
15101                     if ($this->rtl) {
15102                         $this->x = $this->w - $this->x - $lspace;
15103                     } else {
15104                         $this->x -= $lspace;
15105                     }
15106                     $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
15107                     break;
15108                 }
15109                 case 'square': {
15110                     $l = $size / 3;
15111                     $lspace += $l;
15112                     if ($this->rtl) {
15113                         $this->x = $this->w - $this->x - $lspace;
15114                     } else {
15115                         $this->x -= $lspace;
15116                     }
15117                     $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color);
15118                     break;
15119                 }
15120                 // ordered types
15121 
15122                 // $this->listcount[$this->listnum];
15123                 // $textitem
15124                 case '1':
15125                 case 'decimal': {
15126                     $textitem = $this->listcount[$this->listnum];
15127                     break;
15128                 }
15129                 case 'decimal-leading-zero': {
15130                     $textitem = sprintf("%02d", $this->listcount[$this->listnum]);
15131                     break;
15132                 }
15133                 case 'i':
15134                 case 'lower-roman': {
15135                     $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
15136                     break;
15137                 }
15138                 case 'I':
15139                 case 'upper-roman': {
15140                     $textitem = $this->intToRoman($this->listcount[$this->listnum]);
15141                     break;
15142                 }
15143                 case 'a':
15144                 case 'lower-alpha':
15145                 case 'lower-latin': {
15146                     $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
15147                     break;
15148                 }
15149                 case 'A':
15150                 case 'upper-alpha':
15151                 case 'upper-latin': {
15152                     $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
15153                     break;
15154                 }
15155                 case 'lower-greek': {
15156                     $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
15157                     break;
15158                 }
15159                 /*
15160                 // Types to be implemented (special handling)
15161                 case 'hebrew': {
15162                     break;
15163                 }
15164                 case 'armenian': {
15165                     break;
15166                 }
15167                 case 'georgian': {
15168                     break;
15169                 }
15170                 case 'cjk-ideographic': {
15171                     break;
15172                 }
15173                 case 'hiragana': {
15174                     break;
15175                 }
15176                 case 'katakana': {
15177                     break;
15178                 }
15179                 case 'hiragana-iroha': {
15180                     break;
15181                 }
15182                 case 'katakana-iroha': {
15183                     break;
15184                 }
15185                 */
15186                 default: {
15187                     $textitem = $this->listcount[$this->listnum];
15188                 }
15189             }
15190             if (!$this->empty_string($textitem)) {
15191                 // print ordered item
15192                 if ($this->rtl) {
15193                     $textitem = '.'.$textitem;
15194                 } else {
15195                     $textitem = $textitem.'.';
15196                 }
15197                 $lspace += $this->GetStringWidth($textitem);
15198                 if ($this->rtl) {
15199                     $this->x += $lspace;
15200                 } else {
15201                     $this->x -= $lspace;
15202                 }
15203                 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
15204             }
15205             $this->x = $tmpx;
15206             $this->lispacer = '';
15207         }
15208 
15215         protected function getGraphicVars() {
15216             $grapvars = array(
15217                 'FontFamily' => $this->FontFamily,
15218                 'FontStyle' => $this->FontStyle,
15219                 'FontSizePt' => $this->FontSizePt,
15220                 'rMargin' => $this->rMargin,
15221                 'lMargin' => $this->lMargin,
15222                 'cMargin' => $this->cMargin,
15223                 'LineWidth' => $this->LineWidth,
15224                 'linestyleWidth' => $this->linestyleWidth,
15225                 'linestyleCap' => $this->linestyleCap,
15226                 'linestyleJoin' => $this->linestyleJoin,
15227                 'linestyleDash' => $this->linestyleDash,
15228                 'DrawColor' => $this->DrawColor,
15229                 'FillColor' => $this->FillColor,
15230                 'TextColor' => $this->TextColor,
15231                 'ColorFlag' => $this->ColorFlag,
15232                 'bgcolor' => $this->bgcolor,
15233                 'fgcolor' => $this->fgcolor,
15234                 'htmlvspace' => $this->htmlvspace,
15235                 'lasth' => $this->lasth
15236                 );
15237             return $grapvars;
15238         }
15239 
15246         protected function setGraphicVars($gvars) {
15247             $this->FontFamily = $gvars['FontFamily'];
15248             $this->FontStyle = $gvars['FontStyle'];
15249             $this->FontSizePt = $gvars['FontSizePt'];
15250             $this->rMargin = $gvars['rMargin'];
15251             $this->lMargin = $gvars['lMargin'];
15252             $this->cMargin = $gvars['cMargin'];
15253             $this->LineWidth = $gvars['LineWidth'];
15254             $this->linestyleWidth = $gvars['linestyleWidth'];
15255             $this->linestyleCap = $gvars['linestyleCap'];
15256             $this->linestyleJoin = $gvars['linestyleJoin'];
15257             $this->linestyleDash = $gvars['linestyleDash'];
15258             $this->DrawColor = $gvars['DrawColor'];
15259             $this->FillColor = $gvars['FillColor'];
15260             $this->TextColor = $gvars['TextColor'];
15261             $this->ColorFlag = $gvars['ColorFlag'];
15262             $this->bgcolor = $gvars['bgcolor'];
15263             $this->fgcolor = $gvars['fgcolor'];
15264             $this->htmlvspace = $gvars['htmlvspace'];
15265             //$this->lasth = $gvars['lasth'];
15266             $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
15267             if (!$this->empty_string($this->FontFamily)) {
15268                 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
15269             }
15270         }
15271 
15279         protected function getObjFilename($name) {
15280             return tempnam(K_PATH_CACHE, $name.'_');
15281         }
15282 
15291         protected function writeDiskCache($filename, $data, $append=false) {
15292             if ($append) {
15293                 $fmode = 'ab+';
15294             } else {
15295                 $fmode = 'wb+';
15296             }
15297             $f = @fopen($filename, $fmode);
15298             if (!$f) {
15299                 $this->Error('Unable to write cache file: '.$filename);
15300             } else {
15301                 fwrite($f, $data);
15302                 fclose($f);
15303             }
15304             // update file lenght (needed for transactions)
15305             if (!isset($this->cache_file_lenght['_'.$filename])) {
15306                 $this->cache_file_lenght['_'.$filename] = strlen($data);
15307             } else {
15308                 $this->cache_file_lenght['_'.$filename] += strlen($data);
15309             }
15310         }
15311 
15319         protected function readDiskCache($filename) {
15320             return file_get_contents($filename);
15321         }
15322 
15329         protected function setBuffer($data) {
15330             $this->bufferlen += strlen($data);
15331             if ($this->diskcache) {
15332                 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
15333                     $this->buffer = $this->getObjFilename('buffer');
15334                 }
15335                 $this->writeDiskCache($this->buffer, $data, true);
15336             } else {
15337                 $this->buffer .= $data;
15338             }
15339         }
15340 
15347         protected function getBuffer() {
15348             if ($this->diskcache) {
15349                 return $this->readDiskCache($this->buffer);
15350             } else {
15351                 return $this->buffer;
15352             }
15353         }
15354 
15363         protected function setPageBuffer($page, $data, $append=false) {
15364             if ($this->diskcache) {
15365                 if (!isset($this->pages[$page])) {
15366                     $this->pages[$page] = $this->getObjFilename('page'.$page);
15367                 }
15368                 $this->writeDiskCache($this->pages[$page], $data, $append);
15369             } else {
15370                 if ($append) {
15371                     $this->pages[$page] .= $data;
15372                 } else {
15373                     $this->pages[$page] = $data;
15374                 }
15375             }
15376             if ($append AND isset($this->pagelen[$page])) {
15377                 $this->pagelen[$page] += strlen($data);
15378             } else {
15379                 $this->pagelen[$page] = strlen($data);
15380             }
15381         }
15382 
15390         protected function getPageBuffer($page) {
15391             if ($this->diskcache) {
15392                 return $this->readDiskCache($this->pages[$page]);
15393             } elseif (isset($this->pages[$page])) {
15394                 return $this->pages[$page];
15395             }
15396             return false;
15397         }
15398 
15406         protected function setImageBuffer($image, $data) {
15407             if ($this->diskcache) {
15408                 if (!isset($this->images[$image])) {
15409                     $this->images[$image] = $this->getObjFilename('image'.$image);
15410                 }
15411                 $this->writeDiskCache($this->images[$image], serialize($data));
15412             } else {
15413                 $this->images[$image] = $data;
15414             }
15415             if (!in_array($image, $this->imagekeys)) {
15416                 $this->imagekeys[] = $image;
15417             }
15418             ++$this->numimages;
15419         }
15420 
15429         protected function setImageSubBuffer($image, $key, $data) {
15430             if (!isset($this->images[$image])) {
15431                 $this->setImageBuffer($image, array());
15432             }
15433             if ($this->diskcache) {
15434                 $tmpimg = $this->getImageBuffer($image);
15435                 $tmpimg[$key] = $data;
15436                 $this->writeDiskCache($this->images[$image], serialize($tmpimg));
15437             } else {
15438                 $this->images[$image][$key] = $data;
15439             }
15440         }
15441 
15449         protected function getImageBuffer($image) {
15450             if ($this->diskcache AND isset($this->images[$image])) {
15451                 return unserialize($this->readDiskCache($this->images[$image]));
15452             } elseif (isset($this->images[$image])) {
15453                 return $this->images[$image];
15454             }
15455             return false;
15456         }
15457 
15465         protected function setFontBuffer($font, $data) {
15466             if ($this->diskcache) {
15467                 if (!isset($this->fonts[$font])) {
15468                     $this->fonts[$font] = $this->getObjFilename('font');
15469                 }
15470                 $this->writeDiskCache($this->fonts[$font], serialize($data));
15471             } else {
15472                 $this->fonts[$font] = $data;
15473             }
15474             if (!in_array($font, $this->fontkeys)) {
15475                 $this->fontkeys[] = $font;
15476             }
15477         }
15478 
15487         protected function setFontSubBuffer($font, $key, $data) {
15488             if (!isset($this->fonts[$font])) {
15489                 $this->setFontBuffer($font, array());
15490             }
15491             if ($this->diskcache) {
15492                 $tmpfont = $this->getFontBuffer($font);
15493                 $tmpfont[$key] = $data;
15494                 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
15495             } else {
15496                 $this->fonts[$font][$key] = $data;
15497             }
15498         }
15499 
15507         protected function getFontBuffer($font) {
15508             if ($this->diskcache AND isset($this->fonts[$font])) {
15509                 return unserialize($this->readDiskCache($this->fonts[$font]));
15510             } elseif (isset($this->fonts[$font])) {
15511                 return $this->fonts[$font];
15512             }
15513             return false;
15514         }
15515 
15524         public function movePage($frompage, $topage) {
15525             if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
15526                 return false;
15527             }
15528             if ($frompage == $this->page) {
15529                 // close the page before moving it
15530                 $this->endPage();
15531             }
15532             // move all page-related states
15533             $tmppage = $this->pages[$frompage];
15534             $tmppagedim = $this->pagedim[$frompage];
15535             $tmppagelen = $this->pagelen[$frompage];
15536             $tmpintmrk = $this->intmrk[$frompage];
15537             if (isset($this->footerpos[$frompage])) {
15538                 $tmpfooterpos = $this->footerpos[$frompage];
15539             }
15540             if (isset($this->footerlen[$frompage])) {
15541                 $tmpfooterlen = $this->footerlen[$frompage];
15542             }
15543             if (isset($this->transfmrk[$frompage])) {
15544                 $tmptransfmrk = $this->transfmrk[$frompage];
15545             }
15546             if (isset($this->PageAnnots[$frompage])) {
15547                 $tmpannots = $this->PageAnnots[$frompage];
15548             }
15549             if (isset($this->newpagegroup[$frompage])) {
15550                 $tmpnewpagegroup = $this->newpagegroup[$frompage];
15551             }
15552             for ($i = $frompage; $i > $topage; --$i) {
15553                 $j = $i - 1;
15554                 // shift pages down
15555                 $this->pages[$i] = $this->pages[$j];
15556                 $this->pagedim[$i] = $this->pagedim[$j];
15557                 $this->pagelen[$i] = $this->pagelen[$j];
15558                 $this->intmrk[$i] = $this->intmrk[$j];
15559                 if (isset($this->footerpos[$j])) {
15560                     $this->footerpos[$i] = $this->footerpos[$j];
15561                 } elseif (isset($this->footerpos[$i])) {
15562                     unset($this->footerpos[$i]);
15563                 }
15564                 if (isset($this->footerlen[$j])) {
15565                     $this->footerlen[$i] = $this->footerlen[$j];
15566                 } elseif (isset($this->footerlen[$i])) {
15567                     unset($this->footerlen[$i]);
15568                 }
15569                 if (isset($this->transfmrk[$j])) {
15570                     $this->transfmrk[$i] = $this->transfmrk[$j];
15571                 } elseif (isset($this->transfmrk[$i])) {
15572                     unset($this->transfmrk[$i]);
15573                 }
15574                 if (isset($this->PageAnnots[$j])) {
15575                     $this->PageAnnots[$i] = $this->PageAnnots[$j];
15576                 } elseif (isset($this->PageAnnots[$i])) {
15577                     unset($this->PageAnnots[$i]);
15578                 }
15579                 if (isset($this->newpagegroup[$j])) {
15580                     $this->newpagegroup[$i] = $this->newpagegroup[$j];
15581                 } elseif (isset($this->newpagegroup[$i])) {
15582                     unset($this->newpagegroup[$i]);
15583                 }
15584             }
15585             $this->pages[$topage] = $tmppage;
15586             $this->pagedim[$topage] = $tmppagedim;
15587             $this->pagelen[$topage] = $tmppagelen;
15588             $this->intmrk[$topage] = $tmpintmrk;
15589             if (isset($tmpfooterpos)) {
15590                 $this->footerpos[$topage] = $tmpfooterpos;
15591             } elseif (isset($this->footerpos[$topage])) {
15592                 unset($this->footerpos[$topage]);
15593             }
15594             if (isset($tmpfooterlen)) {
15595                 $this->footerlen[$topage] = $tmpfooterlen;
15596             } elseif (isset($this->footerlen[$topage])) {
15597                 unset($this->footerlen[$topage]);
15598             }
15599             if (isset($tmptransfmrk)) {
15600                 $this->transfmrk[$topage] = $tmptransfmrk;
15601             } elseif (isset($this->transfmrk[$topage])) {
15602                 unset($this->transfmrk[$topage]);
15603             }
15604             if (isset($tmpannots)) {
15605                 $this->PageAnnots[$topage] = $tmpannots;
15606             } elseif (isset($this->PageAnnots[$topage])) {
15607                 unset($this->PageAnnots[$topage]);
15608             }
15609             if (isset($tmpnewpagegroup)) {
15610                 $this->newpagegroup[$topage] = $tmpnewpagegroup;
15611             } elseif (isset($this->newpagegroup[$topage])) {
15612                 unset($this->newpagegroup[$topage]);
15613             }
15614             // adjust outlines
15615             $tmpoutlines = $this->outlines;
15616             foreach ($tmpoutlines as $key => $outline) {
15617                 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
15618                     $this->outlines[$key]['p'] = $outline['p'] + 1;
15619                 } elseif ($outline['p'] == $frompage) {
15620                     $this->outlines[$key]['p'] = $topage;
15621                 }
15622             }
15623             // adjust links
15624             $tmplinks = $this->links;
15625             foreach ($tmplinks as $key => $link) {
15626                 if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
15627                     $this->links[$key][0] = $link[0] + 1;
15628                 } elseif ($link[0] == $frompage) {
15629                     $this->links[$key][0] = $topage;
15630                 }
15631             }
15632             // adjust javascript
15633             $tmpjavascript = $this->javascript;
15634             global $jfrompage, $jtopage;
15635             $jfrompage = $frompage;
15636             $jtopage = $topage;
15637             $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
15638                 create_function('$matches', 'global $jfrompage, $jtopage;
15639                 $pagenum = intval($matches[3]) + 1;
15640                 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
15641                     $newpage = ($pagenum + 1);
15642                 } elseif ($pagenum == $jfrompage) {
15643                     $newpage = $jtopage;
15644                 } else {
15645                     $newpage = $pagenum;
15646                 }
15647                 --$newpage;
15648                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
15649             // return to last page
15650             $this->lastPage(true);
15651             return true;
15652         }
15653 
15661         public function deletePage($page) {
15662             if ($page > $this->numpages) {
15663                 return false;
15664             }
15665             // delete current page
15666             unset($this->pages[$page]);
15667             unset($this->pagedim[$page]);
15668             unset($this->pagelen[$page]);
15669             unset($this->intmrk[$page]);
15670             if (isset($this->footerpos[$page])) {
15671                 unset($this->footerpos[$page]);
15672             }
15673             if (isset($this->footerlen[$page])) {
15674                 unset($this->footerlen[$page]);
15675             }
15676             if (isset($this->transfmrk[$page])) {
15677                 unset($this->transfmrk[$page]);
15678             }
15679             if (isset($this->PageAnnots[$page])) {
15680                 unset($this->PageAnnots[$page]);
15681             }
15682             if (isset($this->newpagegroup[$page])) {
15683                 unset($this->newpagegroup[$page]);
15684             }
15685             if (isset($this->pageopen[$page])) {
15686                 unset($this->pageopen[$page]);
15687             }
15688             // update remaining pages
15689             for ($i = $page; $i < $this->numpages; ++$i) {
15690                 $j = $i + 1;
15691                 // shift pages
15692                 $this->pages[$i] = $this->pages[$j];
15693                 $this->pagedim[$i] = $this->pagedim[$j];
15694                 $this->pagelen[$i] = $this->pagelen[$j];
15695                 $this->intmrk[$i] = $this->intmrk[$j];
15696                 if (isset($this->footerpos[$j])) {
15697                     $this->footerpos[$i] = $this->footerpos[$j];
15698                 } elseif (isset($this->footerpos[$i])) {
15699                     unset($this->footerpos[$i]);
15700                 }
15701                 if (isset($this->footerlen[$j])) {
15702                     $this->footerlen[$i] = $this->footerlen[$j];
15703                 } elseif (isset($this->footerlen[$i])) {
15704                     unset($this->footerlen[$i]);
15705                 }
15706                 if (isset($this->transfmrk[$j])) {
15707                     $this->transfmrk[$i] = $this->transfmrk[$j];
15708                 } elseif (isset($this->transfmrk[$i])) {
15709                     unset($this->transfmrk[$i]);
15710                 }
15711                 if (isset($this->PageAnnots[$j])) {
15712                     $this->PageAnnots[$i] = $this->PageAnnots[$j];
15713                 } elseif (isset($this->PageAnnots[$i])) {
15714                     unset($this->PageAnnots[$i]);
15715                 }
15716                 if (isset($this->newpagegroup[$j])) {
15717                     $this->newpagegroup[$i] = $this->newpagegroup[$j];
15718                 } elseif (isset($this->newpagegroup[$i])) {
15719                     unset($this->newpagegroup[$i]);
15720                 }
15721                 if (isset($this->pageopen[$j])) {
15722                     $this->pageopen[$i] = $this->pageopen[$j];
15723                 } elseif (isset($this->pageopen[$i])) {
15724                     unset($this->pageopen[$i]);
15725                 }
15726             }
15727             // remove last page
15728             unset($this->pages[$this->numpages]);
15729             unset($this->pagedim[$this->numpages]);
15730             unset($this->pagelen[$this->numpages]);
15731             unset($this->intmrk[$this->numpages]);
15732             if (isset($this->footerpos[$this->numpages])) {
15733                 unset($this->footerpos[$this->numpages]);
15734             }
15735             if (isset($this->footerlen[$this->numpages])) {
15736                 unset($this->footerlen[$this->numpages]);
15737             }
15738             if (isset($this->transfmrk[$this->numpages])) {
15739                 unset($this->transfmrk[$this->numpages]);
15740             }
15741             if (isset($this->PageAnnots[$this->numpages])) {
15742                 unset($this->PageAnnots[$this->numpages]);
15743             }
15744             if (isset($this->newpagegroup[$this->numpages])) {
15745                 unset($this->newpagegroup[$this->numpages]);
15746             }
15747             if (isset($this->pageopen[$this->numpages])) {
15748                 unset($this->pageopen[$this->numpages]);
15749             }
15750             --$this->numpages;
15751             $this->page = $this->numpages;
15752             // adjust outlines
15753             $tmpoutlines = $this->outlines;
15754             foreach ($tmpoutlines as $key => $outline) {
15755                 if ($outline['p'] > $page) {
15756                     $this->outlines[$key]['p'] = $outline['p'] - 1;
15757                 } elseif ($outline['p'] == $page) {
15758                     unset($this->outlines[$key]);
15759                 }
15760             }
15761             // adjust links
15762             $tmplinks = $this->links;
15763             foreach ($tmplinks as $key => $link) {
15764                 if ($link[0] > $page) {
15765                     $this->links[$key][0] = $link[0] - 1;
15766                 } elseif ($link[0] == $page) {
15767                     unset($this->links[$key]);
15768                 }
15769             }
15770             // adjust javascript
15771             $tmpjavascript = $this->javascript;
15772             global $jpage;
15773             $jpage = $page;
15774             $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
15775                 create_function('$matches', 'global $jpage;
15776                 $pagenum = intval($matches[3]) + 1;
15777                 if ($pagenum >= $jpage) {
15778                     $newpage = ($pagenum - 1);
15779                 } elseif ($pagenum == $jpage) {
15780                     $newpage = 1;
15781                 } else {
15782                     $newpage = $pagenum;
15783                 }
15784                 --$newpage;
15785                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
15786             // return to last page
15787             $this->lastPage(true);
15788             return true;
15789         }
15790 
15801         public function addTOC($page='', $numbersfont='', $filler='.') {
15802             $fontsize = $this->FontSizePt;
15803             $fontfamily = $this->FontFamily;
15804             $fontstyle = $this->FontStyle;
15805             $w = $this->w - $this->lMargin - $this->rMargin;
15806             $spacer = $this->GetStringWidth(' ') * 4;
15807             $page_first = $this->getPage();
15808             $lmargin = $this->lMargin;
15809             $rmargin = $this->rMargin;
15810             $x_start = $this->GetX();
15811             if ($this->empty_string($numbersfont)) {
15812                 $numbersfont = $this->default_monospaced_font;
15813             }
15814             if ($this->empty_string($filler)) {
15815                 $filler = ' ';
15816             }
15817             if ($this->empty_string($page)) {
15818                 $gap = ' ';
15819             } else {
15820                 $gap = '';
15821             }
15822             foreach ($this->outlines as $key => $outline) {
15823                 if ($this->rtl) {
15824                     $aligntext = 'R';
15825                     $alignnum = 'L';
15826                 } else {
15827                     $aligntext = 'L';
15828                     $alignnum = 'R';
15829                 }
15830                 if ($outline['l'] == 0) {
15831                     $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
15832                 } else {
15833                     $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
15834                 }
15835                 $indent = ($spacer * $outline['l']);
15836                 if ($this->rtl) {
15837                     $this->rMargin += $indent;
15838                     $this->x -= $indent;
15839                 } else {
15840                     $this->lMargin += $indent;
15841                     $this->x += $indent;
15842                 }
15843                 $link = $this->AddLink();
15844                 $this->SetLink($link, 0, $outline['p']);
15845                 // write the text
15846                 $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
15847                 $this->SetFont($numbersfont, $fontstyle, $fontsize);
15848                 if ($this->empty_string($page)) {
15849                     $pagenum = $outline['p'];
15850                 } else {
15851                     // placemark to be replaced with the correct number
15852                     $pagenum = '{#'.($outline['p']).'}';
15853                     if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
15854                         $pagenum = '{'.$pagenum.'}';
15855                     }
15856                 }
15857                 $numwidth = $this->GetStringWidth($pagenum);
15858                 if ($this->rtl) {
15859                     $tw = $this->x - $this->lMargin;
15860                 } else {
15861                     $tw = $this->w - $this->rMargin - $this->x;
15862                 }
15863                 $fw = $tw - $numwidth - $this->GetStringWidth(' ');
15864                 $numfills = floor($fw / $this->GetStringWidth($filler));
15865                 if ($numfills > 0) {
15866                     $rowfill = str_repeat($filler, $numfills);
15867                 } else {
15868                     $rowfill = '';
15869                 }
15870                 if ($this->rtl) {
15871                     $pagenum = $pagenum.$gap.$rowfill.' ';
15872                 } else {
15873                     $pagenum = ' '.$rowfill.$gap.$pagenum;
15874                 }
15875                 // write the number
15876                 //$this->SetX($x_start);
15877                 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
15878                 $this->SetX($x_start);
15879                 $this->lMargin = $lmargin;
15880                 $this->rMargin = $rmargin;
15881             }
15882             $page_last = $this->getPage();
15883             $numpages = $page_last - $page_first + 1;
15884             if (!$this->empty_string($page)) {
15885                 for ($p = $page_first; $p <= $page_last; ++$p) {
15886                     // get page data
15887                     $temppage = $this->getPageBuffer($p);
15888                     for ($n = 1; $n <= $this->numpages; ++$n) {
15889                         // update page numbers
15890                         $k = '{#'.$n.'}';
15891                         $ku = '{'.$k.'}';
15892                         $alias_a = $this->_escape($k);
15893                         $alias_au = $this->_escape('{'.$k.'}');
15894                         if ($this->isunicode) {
15895                             $alias_b = $this->_escape($this->UTF8ToLatin1($k));
15896                             $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
15897                             $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
15898                             $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
15899                         }
15900                         if ($n >= $page) {
15901                             $np = $n + $numpages;
15902                         } else {
15903                             $np = $n;
15904                         }
15905                         $ns = $this->formatTOCPageNumber($np);
15906                         $nu = $ns;
15907                         $sdiff = strlen($k) - strlen($ns) - 1;
15908                         $sdiffu = strlen($ku) - strlen($ns) - 1;
15909                         $sfill = str_repeat($filler, $sdiff);
15910                         $sfillu = str_repeat($filler, $sdiffu);
15911                         if ($this->rtl) {
15912                             $ns = $ns.' '.$sfill;
15913                             $nu = $nu.' '.$sfillu;
15914                         } else {
15915                             $ns = $sfill.' '.$ns;
15916                             $nu = $sfillu.' '.$nu;
15917                         }
15918                         $nu = $this->UTF8ToUTF16BE($nu, false);
15919                         $temppage = str_replace($alias_au, $nu, $temppage);
15920                         if ($this->isunicode) {
15921                             $temppage = str_replace($alias_bu, $nu, $temppage);
15922                             $temppage = str_replace($alias_cu, $nu, $temppage);
15923                             $temppage = str_replace($alias_b, $ns, $temppage);
15924                             $temppage = str_replace($alias_c, $ns, $temppage);
15925                         }
15926                         $temppage = str_replace($alias_a, $ns, $temppage);
15927                     }
15928                     // save changes
15929                     $this->setPageBuffer($p, $temppage);
15930                 }
15931                 // move pages
15932                 for ($i = 0; $i < $numpages; ++$i) {
15933                     $this->movePage($page_last, $page);
15934                 }
15935             }
15936             $this->SetFont($fontfamily, $fontstyle, $fontsize);
15937         }
15938 
15944         public function startTransaction() {
15945             if (isset($this->objcopy)) {
15946                 // remove previous copy
15947                 $this->commitTransaction();
15948             }
15949             // record current page number
15950             $this->start_transaction_page = $this->page;
15951             // clone current object
15952             $this->objcopy = $this->objclone($this);
15953         }
15954 
15960         public function commitTransaction() {
15961             if (isset($this->objcopy)) {
15962                 $this->objcopy->_destroy(true, true);
15963                 unset($this->objcopy);
15964             }
15965         }
15966 
15974         public function rollbackTransaction($self=false) {
15975             if (isset($this->objcopy)) {
15976                 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
15977                     // truncate files to previous values
15978                     foreach ($this->objcopy->cache_file_lenght as $file => $lenght) {
15979                         $file = substr($file, 1);
15980                         $handle = fopen($file, 'r+');
15981                         ftruncate($handle, $lenght);
15982                     }
15983                 }
15984                 $this->_destroy(true, true);
15985                 if ($self) {
15986                     $objvars = get_object_vars($this->objcopy);
15987                     foreach ($objvars as $key => $value) {
15988                         $this->$key = $value;
15989                     }
15990                 }
15991                 return $this->objcopy;
15992             }
15993             return $this;
15994         }
15995 
16003         public function objclone($object) {
16004             return @clone($object);
16005         }
16006 
16014         public function empty_string($str) {
16015             return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
16016         }
16017         
16018     } // END OF TCPDF CLASS
16019 }
16020 //============================================================+
16021 // END OF FILE
16022 //============================================================+
16023 ?>