. // // See LICENSE.TXT file for more information. // ---------------------------------------------------------------------------- // // Description : This is a PHP class for generating PDF documents without // requiring external extensions. // // NOTE: // This class was originally derived in 2002 from the Public // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), // but now is almost entirely rewritten. // // Main features: // * no external libraries are required for the basic functions; // * supports all ISO page formats; // * supports custom page formats, margins and units of measure; // * supports UTF-8 Unicode and Right-To-Left languages; // * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts; // * supports document encryption; // * includes methods to publish some XHTML code; // * includes graphic (geometric) and transformation methods; // * includes Javascript and forms support; // * 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; // * includes methods to set Bookmarks and print a Table of Content; // * includes methods to move and delete pages; // * includes methods for automatic page header and footer management; // * supports automatic page break; // * supports automatic page numbering and page groups; // * supports automatic line break and text justification; // * 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) // * supports stroke and clipping mode for text; // * supports clipping masks; // * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies; // * supports several annotations, including links, text and file attachments; // * supports page compression (requires zlib extension); // * supports text hyphenation. // * supports transactions to UNDO commands. // // ----------------------------------------------------------- // THANKS TO: // // Olivier Plathey (http://www.fpdf.org) for original FPDF. // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support. // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm. // Warren Sherliker (wsherliker@gmail.com) for better image handling. // dullus for text Justification. // Bob Vincent (pillarsdotnet@users.sourceforge.net) for
* Char. number range | UTF-8 octet sequence * (hexadecimal) | (binary) * --------------------+----------------------------------------------- * 0000 0000-0000 007F | 0xxxxxxx * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * --------------------------------------------------------------------- * * ABFN notation: * --------------------------------------------------------------------- * UTF8-octets = *( UTF8-char ) * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 * UTF8-1 = %x00-7F * UTF8-2 = %xC2-DF UTF8-tail * * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / * %xF4 %x80-8F 2( UTF8-tail ) * UTF8-tail = %x80-BF * --------------------------------------------------------------------- ** @param string $str string to process. * @return array containing codepoints (UTF-8 characters values) * @access protected * @author Nicola Asuni * @since 1.53.0.TC005 (2005-01-05) */ protected function UTF8StringToArray($str) { if (isset($this->cache_UTF8StringToArray['_'.$str])) { // return cached value return($this->cache_UTF8StringToArray['_'.$str]); } // check cache size if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) { // remove first element array_shift($this->cache_UTF8StringToArray); } ++$this->cache_size_UTF8StringToArray; if (!$this->isunicode) { // split string into array of equivalent codes $strarr = array(); $strlen = strlen($str); for ($i=0; $i < $strlen; ++$i) { $strarr[] = ord($str{$i}); } // insert new value on cache $this->cache_UTF8StringToArray['_'.$str] = $strarr; return $strarr; } $unicode = array(); // array containing unicode values $bytes = array(); // array containing single character byte sequences $numbytes = 1; // number of octetc needed to represent the UTF-8 character $str .= ''; // force $str to be a string $length = strlen($str); for ($i = 0; $i < $length; ++$i) { $char = ord($str{$i}); // get one string character at time if (count($bytes) == 0) { // get starting octect if ($char <= 0x7F) { $unicode[] = $char; // use the character "as is" because is ASCII $numbytes = 1; } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) $bytes[] = ($char - 0xC0) << 0x06; $numbytes = 2; } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) $bytes[] = ($char - 0xE0) << 0x0C; $numbytes = 3; } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) $bytes[] = ($char - 0xF0) << 0x12; $numbytes = 4; } else { // use replacement character for other invalid sequences $unicode[] = 0xFFFD; $bytes = array(); $numbytes = 1; } } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN $bytes[] = $char - 0x80; if (count($bytes) == $numbytes) { // compose UTF-8 bytes to a single unicode value $char = $bytes[0]; for ($j = 1; $j < $numbytes; ++$j) { $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); } if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { /* The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF, which are reserved for use with the UTF-16 encoding form (as surrogate pairs) and do not directly represent characters. */ $unicode[] = 0xFFFD; // use replacement character } else { $unicode[] = $char; // add char to array } // reset data for next char $bytes = array(); $numbytes = 1; } } else { // use replacement character for other invalid sequences $unicode[] = 0xFFFD; $bytes = array(); $numbytes = 1; } } // insert new value on cache $this->cache_UTF8StringToArray['_'.$str] = $unicode; return $unicode; } /** * Converts UTF-8 strings to UTF16-BE.
* Encoding UTF-16: * * Encoding of a single character from an ISO 10646 character value to * UTF-16 proceeds as follows. Let U be the character number, no greater * than 0x10FFFF. * * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and * terminate. * * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, * U' must be less than or equal to 0xFFFFF. That is, U' can be * represented in 20 bits. * * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and * 0xDC00, respectively. These integers each have 10 bits free to * encode the character value, for a total of 20 bits. * * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order * bits of W1 and the 10 low-order bits of U' to the 10 low-order * bits of W2. Terminate. * * Graphically, steps 2 through 4 look like: * U' = yyyyyyyyyyxxxxxxxxxx * W1 = 110110yyyyyyyyyy * W2 = 110111xxxxxxxxxx ** @param array $unicode array containing UTF-8 unicode values * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) * @return string * @access protected * @author Nicola Asuni * @since 2.1.000 (2008-01-08) * @see UTF8ToUTF16BE() */ protected function arrUTF8ToUTF16BE($unicode, $setbom=true) { $outstr = ''; // string to be returned if ($setbom) { $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) } foreach ($unicode as $char) { if ($char == 0xFFFD) { $outstr .= "\xFF\xFD"; // replacement character } elseif ($char < 0x10000) { $outstr .= chr($char >> 0x08); $outstr .= chr($char & 0xFF); } else { $char -= 0x10000; $w1 = 0xD800 | ($char >> 0x10); $w2 = 0xDC00 | ($char & 0x3FF); $outstr .= chr($w1 >> 0x08); $outstr .= chr($w1 & 0xFF); $outstr .= chr($w2 >> 0x08); $outstr .= chr($w2 & 0xFF); } } return $outstr; } // ==================================================== /** * Set header font. * @param array $font font * @access public * @since 1.1 */ public function setHeaderFont($font) { $this->header_font = $font; } /** * Get header font. * @return array() * @access public * @since 4.0.012 (2008-07-24) */ public function getHeaderFont() { return $this->header_font; } /** * Set footer font. * @param array $font font * @access public * @since 1.1 */ public function setFooterFont($font) { $this->footer_font = $font; } /** * Get Footer font. * @return array() * @access public * @since 4.0.012 (2008-07-24) */ public function getFooterFont() { return $this->footer_font; } /** * Set language array. * @param array $language * @access public * @since 1.1 */ public function setLanguageArray($language) { $this->l = $language; if (isset($this->l['a_meta_dir'])) { $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; } else { $this->rtl = false; } } /** * Returns the PDF data. * @access public */ public function getPDFData() { if ($this->state < 3) { $this->Close(); } return $this->buffer; } /** * Output anchor link. * @param string $url link URL or internal link (i.e.: <a href="#23">link to page 23</a>) * @param string $name link name * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. * @param boolean $firstline if true prints only the first line and return the remaining string. * @param array $color array of RGB text color * @param string $style font style (U, D, B, I) * @return the number of cells used or the remaining text if $firstline = true; * @access public */ public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) { if (!$this->empty_string($url) AND ($url{0} == '#')) { // convert url to internal link $page = intval(substr($url, 1)); $url = $this->AddLink(); $this->SetLink($url, 0, $page); } // store current settings $prevcolor = $this->fgcolor; $prevstyle = $this->FontStyle; if (empty($color)) { $this->SetTextColorArray($this->htmlLinkColorArray); } else { $this->SetTextColorArray($color); } if ($style == -1) { $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle); } else { $this->SetFont('', $this->FontStyle.$style); } $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline); // restore settings $this->SetFont('', $prevstyle); $this->SetTextColorArray($prevcolor); return $ret; } /** * Returns an associative array (keys: R,G,B) from an html color name or a six-digit or three-digit hexadecimal color representation (i.e. #3FE5AA or #7FF). * @param string $color html color * @return array RGB color or false in case of error. * @access public */ public function convertHTMLColorToDec($color='#FFFFFF') { global $webcolor; $returncolor = false; $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces $color = strtolower($color); if (strlen($color) == 0) { return false; } if (substr($color, 0, 3) == 'rgb') { $codes = substr($color, 4); $codes = str_replace(')', '', $codes); $returncolor = explode(',', $codes, 3); return $returncolor; } if (substr($color, 0, 1) != '#') { // decode color name if (isset($webcolor[$color])) { $color_code = $webcolor[$color]; } else { return false; } } else { $color_code = substr($color, 1); } switch (strlen($color_code)) { case 3: { // three-digit hexadecimal representation $r = substr($color_code, 0, 1); $g = substr($color_code, 1, 1); $b = substr($color_code, 2, 1); $returncolor['R'] = hexdec($r.$r); $returncolor['G'] = hexdec($g.$g); $returncolor['B'] = hexdec($b.$b); break; } case 6: { // six-digit hexadecimal representation $returncolor['R'] = hexdec(substr($color_code, 0, 2)); $returncolor['G'] = hexdec(substr($color_code, 2, 2)); $returncolor['B'] = hexdec(substr($color_code, 4, 2)); break; } } return $returncolor; } /** * Converts pixels to User's Units. * @param int $px pixels * @return float value in user's unit * @access public * @see setImageScale(), getImageScale() */ public function pixelsToUnits($px) { return ($px / ($this->imgscale * $this->k)); } /** * Reverse function for htmlentities. * Convert entities in UTF-8. * @param $text_to_convert Text to convert. * @return string converted * @access public */ public function unhtmlentities($text_to_convert) { return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); } // ENCRYPTION METHODS ---------------------------------- // SINCE 2.0.000 (2008-01-02) /** * Compute encryption key depending on object number where the encrypted data is stored * @param int $n object number * @access protected * @since 2.0.000 (2008-01-02) */ protected function _objectkey($n) { return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10); } /** * Put encryption on PDF document. * @access protected * @since 2.0.000 (2008-01-02) */ protected function _putencryption() { $this->_out('/Filter /Standard'); $this->_out('/V 1'); $this->_out('/R 2'); $this->_out('/O ('.$this->_escape($this->Ovalue).')'); $this->_out('/U ('.$this->_escape($this->Uvalue).')'); $this->_out('/P '.$this->Pvalue); } /** * Returns the input text exrypted using RC4 algorithm and the specified key. * RC4 is the standard encryption algorithm used in PDF format * @param string $key encryption key * @param String $text input text to be encrypted * @return String encrypted text * @access protected * @since 2.0.000 (2008-01-02) * @author Klemen Vodopivec */ protected function _RC4($key, $text) { if ($this->last_rc4_key != $key) { $k = str_repeat($key, ((256 / strlen($key)) + 1)); $rc4 = range(0, 255); $j = 0; for ($i = 0; $i < 256; ++$i) { $t = $rc4[$i]; $j = ($j + $t + ord($k{$i})) % 256; $rc4[$i] = $rc4[$j]; $rc4[$j] = $t; } $this->last_rc4_key = $key; $this->last_rc4_key_c = $rc4; } else { $rc4 = $this->last_rc4_key_c; } $len = strlen($text); $a = 0; $b = 0; $out = ''; for ($i = 0; $i < $len; ++$i) { $a = ($a + 1) % 256; $t = $rc4[$a]; $b = ($b + $t) % 256; $rc4[$a] = $rc4[$b]; $rc4[$b] = $t; $k = $rc4[($rc4[$a] + $rc4[$b]) % 256]; $out .= chr(ord($text{$i}) ^ $k); } return $out; } /** * Encrypts a string using MD5 and returns it's value as a binary string. * @param string $str input string * @return String MD5 encrypted binary string * @access protected * @since 2.0.000 (2008-01-02) * @author Klemen Vodopivec */ protected function _md5_16($str) { return pack('H*', md5($str)); } /** * Compute O value (used for RC4 encryption) * @param String $user_pass user password * @param String $owner_pass user password * @return String O value * @access protected * @since 2.0.000 (2008-01-02) * @author Klemen Vodopivec */ protected function _Ovalue($user_pass, $owner_pass) { $tmp = $this->_md5_16($owner_pass); $owner_RC4_key = substr($tmp, 0, 5); return $this->_RC4($owner_RC4_key, $user_pass); } /** * Compute U value (used for RC4 encryption) * @return String U value * @access protected * @since 2.0.000 (2008-01-02) * @author Klemen Vodopivec */ protected function _Uvalue() { return $this->_RC4($this->encryption_key, $this->padding); } /** * Compute encryption key * @param String $user_pass user password * @param String $owner_pass user password * @param String $protection protection type * @access protected * @since 2.0.000 (2008-01-02) * @author Klemen Vodopivec */ protected function _generateencryptionkey($user_pass, $owner_pass, $protection) { // Pad passwords $user_pass = substr($user_pass.$this->padding, 0, 32); $owner_pass = substr($owner_pass.$this->padding, 0, 32); // Compute O value $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass); // Compute encyption key $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF"); $this->encryption_key = substr($tmp, 0, 5); // Compute U value $this->Uvalue = $this->_Uvalue(); // Compute P value $this->Pvalue = -(($protection^255) + 1); } /** * Set document protection * The permission array is composed of values taken from the following ones: * - copy: copy text and images to the clipboard * - print: print the document * - modify: modify it (except for annotations and forms) * - annot-forms: add annotations and forms * Remark: the protection against modification is for people who have the full Acrobat product. * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access. * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts. * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms) * @param String $user_pass user password. Empty by default. * @param String $owner_pass owner password. If not specified, a random value is used. * @access public * @since 2.0.000 (2008-01-02) * @author Klemen Vodopivec */ public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) { $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32); $protection = 192; foreach ($permissions as $permission) { if (!isset($options[$permission])) { $this->Error('Incorrect permission: '.$permission); } $protection += $options[$permission]; } if ($owner_pass === null) { $owner_pass = uniqid(rand()); } $this->encrypted = true; $this->_generateencryptionkey($user_pass, $owner_pass, $protection); } // END OF ENCRYPTION FUNCTIONS ------------------------- // START TRANSFORMATIONS SECTION ----------------------- /** * Starts a 2D tranformation saving current graphic state. * This function must be called before scaling, mirroring, translation, rotation and skewing. * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function StartTransform() { $this->_out('q'); $this->transfmrk[$this->page][] = $this->pagelen[$this->page]; } /** * Stops a 2D tranformation restoring previous graphic state. * This function must be called after scaling, mirroring, translation, rotation and skewing. * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function StopTransform() { $this->_out('Q'); if (isset($this->transfmatrix)) { array_pop($this->transfmatrix); } array_pop($this->transfmrk[$this->page]); } /** * Horizontal Scaling. * @param float $s_x scaling factor for width as percent. 0 is not allowed. * @param int $x abscissa of the scaling center. Default is current x position * @param int $y ordinate of the scaling center. Default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function ScaleX($s_x, $x='', $y='') { $this->Scale($s_x, 100, $x, $y); } /** * Vertical Scaling. * @param float $s_y scaling factor for height as percent. 0 is not allowed. * @param int $x abscissa of the scaling center. Default is current x position * @param int $y ordinate of the scaling center. Default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function ScaleY($s_y, $x='', $y='') { $this->Scale(100, $s_y, $x, $y); } /** * Vertical and horizontal proportional Scaling. * @param float $s scaling factor for width and height as percent. 0 is not allowed. * @param int $x abscissa of the scaling center. Default is current x position * @param int $y ordinate of the scaling center. Default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function ScaleXY($s, $x='', $y='') { $this->Scale($s, $s, $x, $y); } /** * Vertical and horizontal non-proportional Scaling. * @param float $s_x scaling factor for width as percent. 0 is not allowed. * @param float $s_y scaling factor for height as percent. 0 is not allowed. * @param int $x abscissa of the scaling center. Default is current x position * @param int $y ordinate of the scaling center. Default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function Scale($s_x, $s_y, $x='', $y='') { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } if ($this->rtl) { $x = $this->w - $x; } if (($s_x == 0) OR ($s_y == 0)) { $this->Error('Please do not use values equal to zero for scaling'); } $y = ($this->h - $y) * $this->k; $x *= $this->k; //calculate elements of transformation matrix $s_x /= 100; $s_y /= 100; $tm[0] = $s_x; $tm[1] = 0; $tm[2] = 0; $tm[3] = $s_y; $tm[4] = $x * (1 - $s_x); $tm[5] = $y * (1 - $s_y); //scale the coordinate system $this->Transform($tm); } /** * Horizontal Mirroring. * @param int $x abscissa of the point. Default is current x position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function MirrorH($x='') { $this->Scale(-100, 100, $x); } /** * Verical Mirroring. * @param int $y ordinate of the point. Default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function MirrorV($y='') { $this->Scale(100, -100, '', $y); } /** * Point reflection mirroring. * @param int $x abscissa of the point. Default is current x position * @param int $y ordinate of the point. Default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function MirrorP($x='',$y='') { $this->Scale(-100, -100, $x, $y); } /** * Reflection against a straight line through point (x, y) with the gradient angle (angle). * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line). * @param int $x abscissa of the point. Default is current x position * @param int $y ordinate of the point. Default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function MirrorL($angle=0, $x='',$y='') { $this->Scale(-100, 100, $x, $y); $this->Rotate(-2*($angle-90), $x, $y); } /** * Translate graphic object horizontally. * @param int $t_x movement to the right (or left for RTL) * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function TranslateX($t_x) { $this->Translate($t_x, 0); } /** * Translate graphic object vertically. * @param int $t_y movement to the bottom * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function TranslateY($t_y) { $this->Translate(0, $t_y); } /** * Translate graphic object horizontally and vertically. * @param int $t_x movement to the right * @param int $t_y movement to the bottom * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function Translate($t_x, $t_y) { if ($this->rtl) { $t_x = -$t_x; } //calculate elements of transformation matrix $tm[0] = 1; $tm[1] = 0; $tm[2] = 0; $tm[3] = 1; $tm[4] = $t_x * $this->k; $tm[5] = -$t_y * $this->k; //translate the coordinate system $this->Transform($tm); } /** * Rotate object. * @param float $angle angle in degrees for counter-clockwise rotation * @param int $x abscissa of the rotation center. Default is current x position * @param int $y ordinate of the rotation center. Default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function Rotate($angle, $x='', $y='') { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } if ($this->rtl) { $x = $this->w - $x; $angle = -$angle; } $y = ($this->h - $y) * $this->k; $x *= $this->k; //calculate elements of transformation matrix $tm[0] = cos(deg2rad($angle)); $tm[1] = sin(deg2rad($angle)); $tm[2] = -$tm[1]; $tm[3] = $tm[0]; $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x); $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x); //rotate the coordinate system around ($x,$y) $this->Transform($tm); } /** * Skew horizontally. * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) * @param int $x abscissa of the skewing center. default is current x position * @param int $y ordinate of the skewing center. default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function SkewX($angle_x, $x='', $y='') { $this->Skew($angle_x, 0, $x, $y); } /** * Skew vertically. * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) * @param int $x abscissa of the skewing center. default is current x position * @param int $y ordinate of the skewing center. default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function SkewY($angle_y, $x='', $y='') { $this->Skew(0, $angle_y, $x, $y); } /** * Skew. * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) * @param int $x abscissa of the skewing center. default is current x position * @param int $y ordinate of the skewing center. default is current y position * @access public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function Skew($angle_x, $angle_y, $x='', $y='') { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } if ($this->rtl) { $x = $this->w - $x; $angle_x = -$angle_x; } if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) { $this->Error('Please use values between -90 and +90 degrees for Skewing.'); } $x *= $this->k; $y = ($this->h - $y) * $this->k; //calculate elements of transformation matrix $tm[0] = 1; $tm[1] = tan(deg2rad($angle_y)); $tm[2] = tan(deg2rad($angle_x)); $tm[3] = 1; $tm[4] = -$tm[2] * $y; $tm[5] = -$tm[1] * $x; //skew the coordinate system $this->Transform($tm); } /** * Apply graphic transformations. * @access protected * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ protected function Transform($tm) { $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); // store transformation matrix $this->transfmatrix[] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]); // update tranformation mark if (end($this->transfmrk[$this->page]) !== false) { $key = key($this->transfmrk[$this->page]); $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page]; } } // END TRANSFORMATIONS SECTION ------------------------- // START GRAPHIC FUNCTIONS SECTION --------------------- // The following section is based on the code provided by David Hernandez Sanz /** * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page. * @param float $width The width. * @access public * @since 1.0 * @see Line(), Rect(), Cell(), MultiCell() */ public function SetLineWidth($width) { //Set line width $this->LineWidth = $width; $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k)); if ($this->page > 0) { $this->_out($this->linestyleWidth); } } /** * Returns the current the line width. * @return int Line width * @access public * @since 2.1.000 (2008-01-07) * @see Line(), SetLineWidth() */ public function GetLineWidth() { return $this->LineWidth; } /** * Set line style. * @param array $style Line style. Array with keys among the following: *
', $html); $html = preg_replace('/]*)>/xi', '', $html); $html = preg_replace('/
'); //replace some blank characters $html = preg_replace('/
]*)>[\n\r\t]+/', '<\\1\\2>', $html); $html = preg_replace('@(\r\n|\r)@', "\n", $html); $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); $html = strtr($html, $repTable); $offset = 0; while (($offset < strlen($html)) AND ($pos = strpos($html, '', $offset)) !== false) { $html_a = substr($html, 0, $offset); $html_b = substr($html, $offset, ($pos - $offset + 6)); while (preg_match("']*)>(.*?)\n(.*?)'si", $html_b)) { // preserve newlines on tag $html_b = preg_replace("''si", "]*)>(.*?)\n(.*?) \\2
\\3", $html_b); } $html = $html_a.$html_b.substr($html, $pos + 6); $offset = strlen($html_a.$html_b); } $html = str_replace("\n", ' ', $html); // remove extra spaces from code $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '\\1>', $html); $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html); $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); $html = preg_replace('/<\/(td|th)>/', '\\1>', $html); $html = preg_replace('/<\/table>([\s]*) /', ' ]+>)/'; // explodes the string $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); // count elements $maxel = count($a); $elkey = 0; $key = 0; // create an array of elements $dom = array(); $dom[$key] = array(); // set first void element $dom[$key]['tag'] = false; $dom[$key]['value'] = ''; $dom[$key]['parent'] = 0; $dom[$key]['fontname'] = $this->FontFamily; $dom[$key]['fontstyle'] = $this->FontStyle; $dom[$key]['fontsize'] = $this->FontSizePt; $dom[$key]['bgcolor'] = false; $dom[$key]['fgcolor'] = $this->fgcolor; $dom[$key]['align'] = ''; $dom[$key]['listtype'] = ''; $thead = false; // true when we are inside the THEAD tag ++$key; $level = array(); array_push($level, 0); // root while ($elkey < $maxel) { $dom[$key] = array(); $element = $a[$elkey]; $dom[$key]['elkey'] = $elkey; if (preg_match($tagpattern, $element)) { // html tag $element = substr($element, 1, -1); // get tag name preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); $tagname = strtolower($tag[1]); // check if we are inside a table header if ($tagname == 'thead') { if ($element{0} == '/') { $thead = false; } else { $thead = true; } ++$elkey; continue; } $dom[$key]['tag'] = true; $dom[$key]['value'] = $tagname; if ($element{0} == '/') { // closing html tag $dom[$key]['opening'] = false; $dom[$key]['parent'] = end($level); array_pop($level); $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname']; $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle']; $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize']; $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor']; $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor']; $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align']; if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) { $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype']; } // set the number of columns in table tag if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) { $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols']; } if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { $dom[($dom[$key]['parent'])]['content'] = ''; for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) { $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']]; } $key = $i; } // store header rows on a new table if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] == true)) { if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) { $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']]; } for ($i = $dom[$key]['parent']; $i <= $key; ++$i) { $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']]; } } if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) { $dom[($dom[$key]['parent'])]['thead'] .= ''; } } else { // opening html tag $dom[$key]['opening'] = true; $dom[$key]['parent'] = end($level); if (substr($element, -1, 1) != '/') { // not self-closing tag array_push($level, $key); $dom[$key]['self'] = false; } else { $dom[$key]['self'] = true; } // copy some values from parent $parentkey = 0; if ($key > 0) { $parentkey = $dom[$key]['parent']; $dom[$key]['fontname'] = $dom[$parentkey]['fontname']; $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle']; $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize']; $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor']; $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor']; $dom[$key]['align'] = $dom[$parentkey]['align']; $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; } // get attributes preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER); $dom[$key]['attribute'] = array(); // reset attribute array while (list($id, $name) = each($attr_array[1])) { $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; } // split style attributes if (isset($dom[$key]['attribute']['style'])) { // get style attributes preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); $dom[$key]['style'] = array(); // reset style attribute array while (list($id, $name) = each($style_array[1])) { $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); } // --- get some style attributes --- if (isset($dom[$key]['style']['font-family'])) { // font family if (isset($dom[$key]['style']['font-family'])) { $fontslist = preg_split('/[,]/', strtolower($dom[$key]['style']['font-family'])); foreach ($fontslist as $font) { $font = trim(strtolower($font)); if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) { $dom[$key]['fontname'] = $font; break; } } } } // list-style-type if (isset($dom[$key]['style']['list-style-type'])) { $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type'])); if ($dom[$key]['listtype'] == 'inherit') { $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; } } // font size if (isset($dom[$key]['style']['font-size'])) { $fsize = trim($dom[$key]['style']['font-size']); switch ($fsize) { // absolute-size case 'xx-small': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4; break; } case 'x-small': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3; break; } case 'small': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2; break; } case 'medium': { $dom[$key]['fontsize'] = $dom[0]['fontsize']; break; } case 'large': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2; break; } case 'x-large': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4; break; } case 'xx-large': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6; break; } // relative-size case 'smaller': { $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3; break; } case 'larger': { $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3; break; } default: { $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true); } } } // font style if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) { $dom[$key]['fontstyle'] .= 'B'; } if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) { $dom[$key]['fontstyle'] .= '"I'; } // font color if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) { $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']); } // background color if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) { $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']); } // text-decoration if (isset($dom[$key]['style']['text-decoration'])) { $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration'])); foreach ($decors as $dec) { $dec = trim($dec); if (!$this->empty_string($dec)) { if ($dec{0} == 'u') { $dom[$key]['fontstyle'] .= 'U'; } elseif ($dec{0} == 'l') { $dom[$key]['fontstyle'] .= 'D'; } } } } // check for width attribute if (isset($dom[$key]['style']['width'])) { $dom[$key]['width'] = $dom[$key]['style']['width']; } // check for height attribute if (isset($dom[$key]['style']['height'])) { $dom[$key]['height'] = $dom[$key]['style']['height']; } // check for text alignment if (isset($dom[$key]['style']['text-align'])) { $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0}); } // check for border attribute if (isset($dom[$key]['style']['border'])) { $dom[$key]['attribute']['border'] = $dom[$key]['style']['border']; } } // check for font tag if ($dom[$key]['value'] == 'font') { // font family if (isset($dom[$key]['attribute']['face'])) { $fontslist = preg_split('/[,]/', strtolower($dom[$key]['attribute']['face'])); foreach ($fontslist as $font) { $font = trim(strtolower($font)); if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) { $dom[$key]['fontname'] = $font; break; } } } // font size if (isset($dom[$key]['attribute']['size'])) { if ($key > 0) { if ($dom[$key]['attribute']['size']{0} == '+') { $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1)); } elseif ($dom[$key]['attribute']['size']{0} == '-') { $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1)); } else { $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); } } else { $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); } } } // force natural alignment for lists if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) { if ($this->rtl) { $dom[$key]['align'] = 'R'; } else { $dom[$key]['align'] = 'L'; } } if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) { $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO; } if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) { $dom[$key]['fontstyle'] .= 'B'; } if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) { $dom[$key]['fontstyle'] .= 'I'; } if ($dom[$key]['value'] == 'u') { $dom[$key]['fontstyle'] .= 'U'; } if ($dom[$key]['value'] == 'del') { $dom[$key]['fontstyle'] .= 'D'; } if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) { $dom[$key]['fontname'] = $this->default_monospaced_font; } if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) { $headsize = (4 - intval($dom[$key]['value']{1})) * 2; $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize; $dom[$key]['fontstyle'] .= 'B'; } if (($dom[$key]['value'] == 'table')) { $dom[$key]['rows'] = 0; // number of rows $dom[$key]['trids'] = array(); // IDs of TR elements $dom[$key]['thead'] = ''; // table header rows } if (($dom[$key]['value'] == 'tr')) { $dom[$key]['cols'] = 0; // store the number of rows on table element ++$dom[($dom[$key]['parent'])]['rows']; // store the TR elements IDs on table element array_push($dom[($dom[$key]['parent'])]['trids'], $key); if ($thead) { $dom[$key]['thead'] = true; } else { $dom[$key]['thead'] = false; } } if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) { if (isset($dom[$key]['attribute']['colspan'])) { $colspan = intval($dom[$key]['attribute']['colspan']); } else { $colspan = 1; } $dom[$key]['attribute']['colspan'] = $colspan; $dom[($dom[$key]['parent'])]['cols'] += $colspan; } // set foreground color attribute if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) { $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']); } // set background color attribute if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) { $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']); } // check for width attribute if (isset($dom[$key]['attribute']['width'])) { $dom[$key]['width'] = $dom[$key]['attribute']['width']; } // check for height attribute if (isset($dom[$key]['attribute']['height'])) { $dom[$key]['height'] = $dom[$key]['attribute']['height']; } // check for text alignment if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) { $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0}); } } // end opening tag } else { // text $dom[$key]['tag'] = false; $dom[$key]['value'] = stripslashes($this->unhtmlentities($element)); $dom[$key]['parent'] = end($level); } ++$elkey; ++$key; } return $dom; } /** * Allows to preserve some HTML formatting (limited support).
* IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul * @param string $html text to display * @param boolean $ln if true add a new line after text (default = true) * @param int $fill Indicates if the background must be painted (true) or transparent (false). * @param boolean $reseth if true reset the last cell height (default false). * @param boolean $cell if true add the default cMargin space to each Write (default false). * @param string $align Allows to center or align the text. Possible values are:* @access public */ public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') { $gvars = $this->getGraphicVars(); // store current values $prevPage = $this->page; $prevlMargin = $this->lMargin; $prevrMargin = $this->rMargin; $curfontname = $this->FontFamily; $curfontstyle = $this->FontStyle; $curfontsize = $this->FontSizePt; $this->newline = true; $minstartliney = $this->y; $yshift = 0; $startlinepage = $this->page; $newline = true; $loop = 0; $curpos = 0; $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul','tcpdf'); $this->premode = false; if (isset($this->PageAnnots[$this->page])) { $pask = count($this->PageAnnots[$this->page]); } else { $pask = 0; } if (isset($this->footerlen[$this->page])) { $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; } else { $this->footerpos[$this->page] = $this->pagelen[$this->page]; } $startlinepos = $this->footerpos[$this->page]; $lalign = $align; $plalign = $align; if ($this->rtl) { $w = $this->x - $this->lMargin; } else { $w = $this->w - $this->rMargin - $this->x; } $w -= (2 * $this->cMargin); if ($cell) { if ($this->rtl) { $this->x -= $this->cMargin; } else { $this->x += $this->cMargin; } } if ($this->customlistindent >= 0) { $this->listindent = $this->customlistindent; } else { $this->listindent = $this->GetStringWidth('0000'); } $this->listnum = 0; if (($this->empty_string($this->lasth)) OR ($reseth)) { //set row height $this->lasth = $this->FontSize * $this->cell_height_ratio; } $dom = $this->getHtmlDomArray($html); $maxel = count($dom); $key = 0; while ($key < $maxel) { if ($dom[$key]['tag'] OR ($key == 0)) { if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) { $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L'; } // vertically align image in line if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['attribute']['height'])) AND ($dom[$key]['attribute']['height'] > 0)) { // get image height $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px'); if (!$this->InFooter) { // check for page break $this->checkPageBreak($imgh); } if ($this->page > $startlinepage) { // fix line splitted over two pages if (isset($this->footerlen[$startlinepage])) { $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; } // line to be moved one page forward $pagebuff = $this->getPageBuffer($startlinepage); $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); $tstart = substr($pagebuff, 0, $startlinepos); $tend = substr($this->getPageBuffer($startlinepage), $curpos); // remove line from previous page $this->setPageBuffer($startlinepage, $tstart.''.$tend); $pagebuff = $this->getPageBuffer($this->page); $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); $tend = substr($pagebuff, $this->cntmrk[$this->page]); // add line start to current page $yshift = $minstartliney - $this->y; $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k)); $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { $next_pask = count($this->PageAnnots[$this->page]); } else { $next_pask = 0; } if (isset($this->PageAnnots[$startlinepage])) { foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { if ($pak >= $pask) { $this->PageAnnots[$this->page][] = $pac; unset($this->PageAnnots[$startlinepage][$pak]); $npak = count($this->PageAnnots[$this->page]) - 1; $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; } } } $pask = $next_pask; $startlinepos = $this->cntmrk[$this->page]; $startlinepage = $this->page; $startliney = $this->y; } $this->y += (($curfontsize / $this->k) - $imgh); $minstartliney = min($this->y, $minstartliney); } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) { // account for different font size $pfontname = $curfontname; $pfontstyle = $curfontstyle; $pfontsize = $curfontsize; $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname; $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle; $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize; if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) { $this->SetFont($fontname, $fontstyle, $fontsize); $this->lasth = $this->FontSize * $this->cell_height_ratio; if (is_numeric($fontsize) AND ($fontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($fontsize != $curfontsize) AND (!$this->newline) AND ($key < ($maxel - 1)) ) { if ((!$this->newline) AND ($this->page > $startlinepage)) { // fix lines splitted over two pages if (isset($this->footerlen[$startlinepage])) { $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; } // line to be moved one page forward $pagebuff = $this->getPageBuffer($startlinepage); $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); $tstart = substr($pagebuff, 0, $startlinepos); $tend = substr($this->getPageBuffer($startlinepage), $curpos); // remove line start from previous page $this->setPageBuffer($startlinepage, $tstart.''.$tend); $pagebuff = $this->getPageBuffer($this->page); $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); $tend = substr($pagebuff, $this->cntmrk[$this->page]); // add line start to current page $yshift = $minstartliney - $this->y; $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k)); $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { $next_pask = count($this->PageAnnots[$this->page]); } else { $next_pask = 0; } if (isset($this->PageAnnots[$startlinepage])) { foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { if ($pak >= $pask) { $this->PageAnnots[$this->page][] = $pac; unset($this->PageAnnots[$startlinepage][$pak]); $npak = count($this->PageAnnots[$this->page]) - 1; $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; } } } $pask = $next_pask; } $this->y += (($curfontsize - $fontsize) / $this->k); $minstartliney = min($this->y, $minstartliney); } $curfontname = $fontname; $curfontstyle = $fontstyle; $curfontsize = $fontsize; } } if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) { $plalign = ''; } // get current position on page buffer $curpos = $this->pagelen[$startlinepage]; if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) { $this->SetFillColorArray($dom[$key]['bgcolor']); $wfill = true; } else { $wfill = $fill | false; } if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) { $this->SetTextColorArray($dom[$key]['fgcolor']); } if (isset($dom[$key]['align'])) { $lalign = $dom[$key]['align']; } if ($this->empty_string($lalign)) { $lalign = $align; } } // align lines if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) { $newline = true; // we are at the beginning of a new line if (isset($startlinex)) { $yshift = $minstartliney - $startliney; if (($yshift > 0) OR ($this->page > $startlinepage)) { $yshift = 0; } if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) { // the last line must be shifted to be aligned as requested $linew = abs($this->endlinex - $startlinex); $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; $midpos = min($opentagpos, $this->footerpos[$startlinepage]); } elseif (isset($opentagpos)) { $midpos = $opentagpos; } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; $midpos = $this->footerpos[$startlinepage]; } else { $midpos = 0; } if ($midpos > 0) { $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); $pend = substr($this->getPageBuffer($startlinepage), $midpos); } else { $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); $pend = ''; } // calculate shifting amount $tw = $w; if ($this->lMargin != $prevlMargin) { $tw += ($prevlMargin - $this->lMargin); } if ($this->rMargin != $prevrMargin) { $tw += ($prevrMargin - $this->rMargin); } $mdiff = abs($tw - $linew); $t_x = 0; if ($plalign == 'C') { if ($this->rtl) { $t_x = -($mdiff / 2); } else { $t_x = ($mdiff / 2); } } elseif (($plalign == 'R') AND (!$this->rtl)) { // right alignment on LTR document $t_x = $mdiff; } elseif (($plalign == 'L') AND ($this->rtl)) { // left alignment on RTL document $t_x = -$mdiff; } elseif (($plalign == 'J') AND ($plalign == $lalign)) { // Justification if ($this->rtl OR $this->tmprtl) { $t_x = $this->lMargin - $this->endlinex; } $no = 0; $ns = 0; $pmidtemp = $pmid; // escape special characters $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); // search spaces if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) { $maxkk = count($lnstring[1]) - 1; for ($kk=0; $kk <= $maxkk; ++$kk) { // restore special characters $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]); $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]); if ($kk == $maxkk) { if ($this->rtl OR $this->tmprtl) { $tvalue = ltrim($lnstring[1][$kk]); } else { $tvalue = rtrim($lnstring[1][$kk]); } } else { $tvalue = $lnstring[1][$kk]; } // count spaces on line $no += substr_count($lnstring[1][$kk], chr(32)); $ns += substr_count($tvalue, chr(32)); } if ($this->rtl OR $this->tmprtl) { $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32))); } // calculate additional space to add to each space $spacewidth = (($tw - $linew + (($no - $ns) * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1)) * $this->k; $spacewidthu = -1000 * ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize; $nsmax = $ns; $ns = 0; reset($lnstring); $offset = 0; $strcount = 0; $prev_epsposbeg = 0; global $spacew; while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) { if ($this->rtl OR $this->tmprtl) { $spacew = ($spacewidth * ($nsmax - $ns)); } else { $spacew = ($spacewidth * $ns); } $offset = $strpiece[2][1] + strlen($strpiece[2][0]); $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q'); if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) { // shift EPS images $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew); $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6)); $pmid_b = substr($pmid, 0, $epsposbeg); $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg)); $pmid_e = substr($pmid, $epsposend); $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e; $offset = $epsposend; continue; } $prev_epsposbeg = $epsposbeg; $currentxpos = 0; // shift blocks of code switch ($strpiece[2][0]) { case 'Td': case 'cm': case 'm': case 'l': { // get current X position preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); $currentxpos = $xmatches[1]; if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) { if ($strcount == $maxkk) { if ($this->rtl OR $this->tmprtl) { $tvalue = $lnstring[1][$strcount]; } else { $tvalue = rtrim($lnstring[1][$strcount]); } } else { $tvalue = $lnstring[1][$strcount]; } $ns += substr_count($tvalue, chr(32)); ++$strcount; } if ($this->rtl OR $this->tmprtl) { $spacew = ($spacewidth * ($nsmax - $ns)); } // justify block $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', create_function('$matches', 'global $spacew; $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew)); return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1); break; } case 're': { // get current X position preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); $currentxpos = $xmatches[1]; // justify block $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', create_function('$matches', 'global $spacew; $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew)); return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1); break; } case 'c': { // get current X position 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); $currentxpos = $xmatches[1]; // justify block $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', create_function('$matches', 'global $spacew; $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew)); $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew)); $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew)); return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1); break; } } // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { foreach ($this->PageAnnots[$this->page] as $pak => $pac) { if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k); $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); break; } } } } // end of while // remove markers $pmid = str_replace('x*#!#*x', '', $pmid); if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { // multibyte characters $spacew = $spacewidthu; $pmidtemp = $pmid; // escape special characters $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x", create_function('$matches', 'global $spacew; $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]); $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]); return "[(".str_replace(chr(0).chr(32), ") ".($spacew)." (", $matches[1]).")]";'), $pmidtemp); $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend); $endlinepos = strlen($pstart."\n".$pmid."\n"); } else { // non-unicode (single-byte characters) $rs = sprintf("%.3F Tw", $spacewidth); $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid); $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend); $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n"); } } } // end of J if (($t_x != 0) OR ($yshift < 0)) { // shift the line $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k)); $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend); $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n"); // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { foreach ($this->PageAnnots[$this->page] as $pak => $pac) { if ($pak >= $pask) { $this->PageAnnots[$this->page][$pak]['x'] += $t_x; $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; } } } $this->y -= $yshift; } } } $this->newline = false; $pbrk = $this->checkPageBreak($this->lasth); $this->SetFont($fontname, $fontstyle, $fontsize); if ($wfill) { $this->SetFillColorArray($this->bgcolor); } $startlinex = $this->x; $startliney = $this->y; $minstartliney = $this->y; $startlinepage = $this->page; if (isset($endlinepos) AND (!$pbrk)) { $startlinepos = $endlinepos; unset($endlinepos); } else { if (isset($this->footerlen[$this->page])) { $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; } else { $this->footerpos[$this->page] = $this->pagelen[$this->page]; } $startlinepos = $this->footerpos[$this->page]; } $plalign = $lalign; if (isset($this->PageAnnots[$this->page])) { $pask = count($this->PageAnnots[$this->page]); } else { $pask = 0; } } if (isset($opentagpos)) { unset($opentagpos); } if ($dom[$key]['tag']) { if ($dom[$key]['opening']) { if ($dom[$key]['value'] == 'table') { if ($this->rtl) { $wtmp = $this->x - $this->lMargin; } else { $wtmp = $this->w - $this->rMargin - $this->x; } $wtmp -= (2 * $this->cMargin); // calculate cell width if (isset($dom[$key]['width'])) { $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); } else { $table_width = $wtmp; } } // table content is handled in a special way if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { $trid = $dom[$key]['parent']; $table_el = $dom[$trid]['parent']; if (!isset($dom[$table_el]['cols'])) { $dom[$table_el]['cols'] = $trid['cols']; } $oldmargin = $this->cMargin; if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px'); } else { $currentcmargin = 0; } $this->cMargin = $currentcmargin; if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) { $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px'); } else { $cellspacing = 0; } if ($this->rtl) { $cellspacingx = -$cellspacing; } else { $cellspacingx = $cellspacing; } $colspan = $dom[$key]['attribute']['colspan']; $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols'])); if (isset($dom[$key]['width'])) { $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); } else { $cellw = $wtmp; } if (isset($dom[$key]['height'])) { // minimum cell height $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px'); } else { $cellh = 0; } $cellw -= $cellspacing; if (isset($dom[$key]['content'])) { $cell_content = $dom[$key]['content']; } else { $cell_content = ' '; } $tagtype = $dom[$key]['value']; $parentid = $key; while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) { // move $key index forward ++$key; } if (!isset($dom[$trid]['startpage'])) { $dom[$trid]['startpage'] = $this->page; } else { $this->setPage($dom[$trid]['startpage']); } if (!isset($dom[$trid]['starty'])) { $dom[$trid]['starty'] = $this->y; } else { $this->y = $dom[$trid]['starty']; } if (!isset($dom[$trid]['startx'])) { $dom[$trid]['startx'] = $this->x; } $this->x += ($cellspacingx / 2); if (isset($dom[$parentid]['attribute']['rowspan'])) { $rowspan = intval($dom[$parentid]['attribute']['rowspan']); } else { $rowspan = 1; } // skip row-spanned cells started on the previous rows if (isset($dom[$table_el]['rowspans'])) { $rsk = 0; $rskmax = count($dom[$table_el]['rowspans']); while ($rsk < $rskmax) { $trwsp = $dom[$table_el]['rowspans'][$rsk]; $rsstartx = $trwsp['startx']; $rsendx = $trwsp['endx']; // account for margin changes if ($trwsp['startpage'] < $this->page) { if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) { $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']); $rsstartx -= $dl; $rsendx -= $dl; } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) { $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']); $rsstartx += $dl; $rsendx += $dl; } } if (($trwsp['rowspan'] > 0) AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps)) AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps)) AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) { // set the starting X position of the current cell $this->x = $rsendx + $cellspacingx; if (($trwsp['rowspan'] == 1) AND (isset($dom[$trid]['endy'])) AND (isset($dom[$trid]['endpage'])) AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) { // set ending Y position for row $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy']; } $rsk = 0; } else { ++$rsk; } } } // add rowspan information to table element if ($rowspan > 1) { if (isset($this->footerlen[$this->page])) { $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; } else { $this->footerpos[$this->page] = $this->pagelen[$this->page]; } $trintmrkpos = $this->footerpos[$this->page]; $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)); } $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x)); if ($rowspan > 1) { $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1); } // push background colors if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) { $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor']; } $prevLastH = $this->lasth; // ****** write the cell content ****** $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true); $this->lasth = $prevLastH; $this->cMargin = $oldmargin; $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x; // update the end of row position if ($rowspan <= 1) { if (isset($dom[$trid]['endy'])) { if ($this->page == $dom[$trid]['endpage']) { $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']); } elseif ($this->page > $dom[$trid]['endpage']) { $dom[$trid]['endy'] = $this->y; } } else { $dom[$trid]['endy'] = $this->y; } if (isset($dom[$trid]['endpage'])) { $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']); } else { $dom[$trid]['endpage'] = $this->page; } } else { // account for row-spanned cells $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x; $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y; $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page; } if (isset($dom[$table_el]['rowspans'])) { // update endy and endpage on rowspanned cells foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { if ($trwsp['rowspan'] > 0) { if (isset($dom[$trid]['endpage'])) { if ($trwsp['endpage'] == $dom[$trid]['endpage']) { $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) { $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy']; $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage']; } else { $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm']; } } } } } $this->x += ($cellspacingx / 2); } else { // opening tag (or self-closing tag) if (!isset($opentagpos)) { if (!$this->InFooter) { if (isset($this->footerlen[$this->page])) { $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; } else { $this->footerpos[$this->page] = $this->pagelen[$this->page]; } $opentagpos = $this->footerpos[$this->page]; } } $this->openHTMLTagHandler($dom, $key, $cell); } } else { // closing tag $this->closeHTMLTagHandler($dom, $key, $cell); } } elseif (strlen($dom[$key]['value']) > 0) { // print list-item if (!$this->empty_string($this->lispacer)) { $this->SetFont($pfontname, $pfontstyle, $pfontsize); $this->lasth = $this->FontSize * $this->cell_height_ratio; $minstartliney = $this->y; $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize); $this->SetFont($curfontname, $curfontstyle, $curfontsize); $this->lasth = $this->FontSize * $this->cell_height_ratio; if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) { $this->y += (($pfontsize - $curfontsize) / $this->k); $minstartliney = min($this->y, $minstartliney); } } // text $this->htmlvspace = 0; if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) { // reverse spaces order $len1 = strlen($dom[$key]['value']); $lsp = $len1 - strlen(ltrim($dom[$key]['value'])); $rsp = $len1 - strlen(rtrim($dom[$key]['value'])); $tmpstr = ''; if ($rsp > 0) { $tmpstr .= substr($dom[$key]['value'], -$rsp); } $tmpstr .= trim($dom[$key]['value']); if ($lsp > 0) { $tmpstr .= substr($dom[$key]['value'], 0, $lsp); } $dom[$key]['value'] = $tmpstr; } if ($newline) { if (!$this->premode) { if (($this->rtl OR $this->tmprtl)) { $dom[$key]['value'] = rtrim($dom[$key]['value']); } else { $dom[$key]['value'] = ltrim($dom[$key]['value']); } } $newline = false; $firstblock = true; } else { $firstblock = false; } $strrest = ''; if (!empty($this->HREF) AND (isset($this->HREF['url']))) { // HTML Link $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']); } else { $ctmpmargin = $this->cMargin; $this->cMargin = 0; // ****** write only until the end of the line and get the rest ****** $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock); $this->cMargin = $ctmpmargin; } if (strlen($strrest) > 0) { // store the remaining string on the previous $key position $this->newline = true; if ($cell) { if ($this->rtl) { $this->x -= $this->cMargin; } else { $this->x += $this->cMargin; } } if ($strrest == $dom[$key]['value']) { // used to avoid infinite loop ++$loop; } else { $loop = 0; } $dom[$key]['value'] = ltrim($strrest); if ($loop < 3) { --$key; } } else { $loop = 0; } } ++$key; } // end for each $key // align the last line if (isset($startlinex)) { $yshift = $minstartliney - $startliney; if (($yshift > 0) OR ($this->page > $startlinepage)) { $yshift = 0; } if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) { // the last line must be shifted to be aligned as requested $linew = abs($this->endlinex - $startlinex); $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; $midpos = min($opentagpos, $this->footerpos[$startlinepage]); } elseif (isset($opentagpos)) { $midpos = $opentagpos; } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; $midpos = $this->footerpos[$startlinepage]; } else { $midpos = 0; } if ($midpos > 0) { $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); $pend = substr($this->getPageBuffer($startlinepage), $midpos); } else { $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); $pend = ''; } // calculate shifting amount $tw = $w; if ($this->lMargin != $prevlMargin) { $tw += ($prevlMargin - $this->lMargin); } if ($this->rMargin != $prevrMargin) { $tw += ($prevrMargin - $this->rMargin); } $mdiff = abs($tw - $linew); if ($plalign == 'C') { if ($this->rtl) { $t_x = -($mdiff / 2); } else { $t_x = ($mdiff / 2); } } elseif (($plalign == 'R') AND (!$this->rtl)) { // right alignment on LTR document $t_x = $mdiff; } elseif (($plalign == 'L') AND ($this->rtl)) { // left alignment on RTL document $t_x = -$mdiff; } else { $t_x = 0; } if (($t_x != 0) OR ($yshift < 0)) { // shift the line $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k)); $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend); $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n"); // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { foreach ($this->PageAnnots[$this->page] as $pak => $pac) { if ($pak >= $pask) { $this->PageAnnots[$this->page][$pak]['x'] += $t_x; $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; } } } $this->y -= $yshift; } } } if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) { $this->Ln($this->lasth); } // restore previous values $this->setGraphicVars($gvars); if ($this->page > $prevPage) { $this->lMargin = $this->pagedim[$this->page]['olm']; $this->rMargin = $this->pagedim[$this->page]['orm']; } unset($dom); } /** * Process opening tags. * @param array $dom html dom array * @param int $key current element id * @param boolean $cell if true add the default cMargin space to each new line (default false). * @access protected */ protected function openHTMLTagHandler(&$dom, $key, $cell=false) { $tag = $dom[$key]; $parent = $dom[($dom[$key]['parent'])]; $firstorlast = ($key == 1); // check for text direction attribute if (isset($tag['attribute']['dir'])) { $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L'; } else { $this->tmprtl = false; } //Opening tag switch($tag['value']) { case 'table': { $cp = 0; $cs = 0; $dom[$key]['rowspans'] = array(); if (!$this->empty_string($dom[$key]['thead'])) { // set table header $this->thead = $dom[$key]['thead']; if (!isset($this->theadMargins) OR (empty($this->theadMargins))) { $this->theadMargins = array(); $this->theadMargins['cmargin'] = $this->cMargin; } } if (isset($tag['attribute']['cellpadding'])) { $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px'); $this->oldcMargin = $this->cMargin; $this->cMargin = $cp; } if (isset($tag['attribute']['cellspacing'])) { $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); } $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth); break; } case 'tr': { // array of columns positions $dom[$key]['cellpos'] = array(); break; } case 'hr': { $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); $this->htmlvspace = 0; $wtmp = $this->w - $this->lMargin - $this->rMargin; if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) { $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px'); } else { $hrWidth = $wtmp; } $x = $this->GetX(); $y = $this->GetY(); $prevlinewidth = $this->GetLineWidth(); $this->Line($x, $y, $x + $hrWidth, $y); $this->SetLineWidth($prevlinewidth); $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false); break; } case 'a': { if (array_key_exists('href', $tag['attribute'])) { $this->HREF['url'] = $tag['attribute']['href']; } $this->HREF['color'] = $this->htmlLinkColorArray; $this->HREF['style'] = $this->htmlLinkFontStyle; if (array_key_exists('style', $tag['attribute'])) { // get style attributes preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER); $astyle = array(); while (list($id, $name) = each($style_array[1])) { $name = strtolower($name); $astyle[$name] = trim($style_array[2][$id]); } if (isset($astyle['color'])) { $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']); } if (isset($astyle['text-decoration'])) { $this->HREF['style'] = ''; $decors = explode(' ', strtolower($astyle['text-decoration'])); foreach ($decors as $dec) { $dec = trim($dec); if (!$this->empty_string($dec)) { if ($dec{0} == 'u') { $this->HREF['style'] .= 'U'; } elseif ($dec{0} == 'l') { $this->HREF['style'] .= 'D'; } } } } } break; } case 'img': { if (isset($tag['attribute']['src'])) { // replace relative path with real server path if ($tag['attribute']['src'][0] == '/') { $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src']; } $tag['attribute']['src'] = urldecode($tag['attribute']['src']); $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']); if (!isset($tag['attribute']['width'])) { $tag['attribute']['width'] = 0; } if (!isset($tag['attribute']['height'])) { $tag['attribute']['height'] = 0; } //if (!isset($tag['attribute']['align'])) { // the only alignment supported is "bottom" // further development is required for other modes. $tag['attribute']['align'] = 'bottom'; //} switch($tag['attribute']['align']) { case 'top': { $align = 'T'; break; } case 'middle': { $align = 'M'; break; } case 'bottom': { $align = 'B'; break; } default: { $align = 'B'; break; } } $fileinfo = pathinfo($tag['attribute']['src']); if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) { $type = strtolower($fileinfo['extension']); } $prevy = $this->y; $xpos = $this->GetX(); if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) { if ($this->rtl) { $xpos += $this->GetStringWidth(' '); } else { $xpos -= $this->GetStringWidth(' '); } } $imglink = ''; if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) { $imglink = $this->HREF['url']; if ($imglink{0} == '#') { // convert url to internal link $page = intval(substr($imglink, 1)); $imglink = $this->AddLink(); $this->SetLink($imglink, 0, $page); } } $border = 0; if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) { // currently only support 1 (frame) or a combination of 'LTRB' $border = $tag['attribute']['border']; } $iw = ''; if (isset($tag['attribute']['width'])) { $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false); } $ih = ''; if (isset($tag['attribute']['height'])) { $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false); } if (($type == 'eps') OR ($type == 'ai')) { $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border); } else { $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border); } switch($align) { case 'T': { $this->y = $prevy; break; } case 'M': { $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ; break; } case 'B': { $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k); break; } } } break; } case 'dl': { ++$this->listnum; $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false); break; } case 'dt': { $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); break; } case 'dd': { if ($this->rtl) { $this->rMargin += $this->listindent; } else { $this->lMargin += $this->listindent; } $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); break; } case 'ul': case 'ol': { $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false); $this->htmlvspace = 0; ++$this->listnum; if ($tag['value'] == 'ol') { $this->listordered[$this->listnum] = true; } else { $this->listordered[$this->listnum] = false; } if (isset($tag['attribute']['start'])) { $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1; } else { $this->listcount[$this->listnum] = 0; } if ($this->rtl) { $this->rMargin += $this->listindent; } else { $this->lMargin += $this->listindent; } $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false); $this->htmlvspace = 0; break; } case 'li': { $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); if ($this->listordered[$this->listnum]) { // ordered item if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { $this->lispacer = $parent['attribute']['type']; } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { $this->lispacer = $parent['listtype']; } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { $this->lispacer = $this->lisymbol; } else { $this->lispacer = '#'; } ++$this->listcount[$this->listnum]; if (isset($tag['attribute']['value'])) { $this->listcount[$this->listnum] = intval($tag['attribute']['value']); } } else { // unordered item if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { $this->lispacer = $parent['attribute']['type']; } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { $this->lispacer = $parent['listtype']; } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { $this->lispacer = $this->lisymbol; } else { $this->lispacer = '!'; } } break; } case 'blockquote': { if ($this->rtl) { $this->rMargin += $this->listindent; } else { $this->lMargin += $this->listindent; } $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false); break; } case 'br': { $this->Ln('', $cell); break; } case 'div': { $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); break; } case 'p': { $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false); break; } case 'pre': { $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); $this->premode = true; break; } case 'sup': { $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k)); break; } case 'sub': { $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k)); break; } case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': { $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false); break; } case 'tcpdf': { // NOT HTML: used to call TCPDF methods if (isset($tag['attribute']['method'])) { $tcpdf_method = $tag['attribute']['method']; if (method_exists($this, $tcpdf_method)) { if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) { eval('$params = array('.$tag['attribute']['params'].');'); call_user_func_array(array($this, $tcpdf_method), $params); } else { $this->$tcpdf_method(); } $this->newline = true; } } } default: { break; } } } /** * Process closing tags. * @param array $dom html dom array * @param int $key current element id * @param boolean $cell if true add the default cMargin space to each new line (default false). * @access protected */ protected function closeHTMLTagHandler(&$dom, $key, $cell=false) { $tag = $dom[$key]; $parent = $dom[($dom[$key]['parent'])]; $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker'))); $in_table_head = false; //Closing tag switch($tag['value']) { case 'tr': { $table_el = $dom[($dom[$key]['parent'])]['parent']; if(!isset($parent['endy'])) { $dom[($dom[$key]['parent'])]['endy'] = $this->y; $parent['endy'] = $this->y; } if(!isset($parent['endpage'])) { $dom[($dom[$key]['parent'])]['endpage'] = $this->page; $parent['endpage'] = $this->page; } // update row-spanned cells if (isset($dom[$table_el]['rowspans'])) { foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1; if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) { $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']); } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) { $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; } } } // report new endy and endpage to the rowspanned cells foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']); $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']); $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; } } // update remaining rowspanned cells foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage']; $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy']; } } } $this->setPage($dom[($dom[$key]['parent'])]['endpage']); $this->y = $dom[($dom[$key]['parent'])]['endy']; if (isset($dom[$table_el]['attribute']['cellspacing'])) { $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px'); $this->y += $cellspacing; } $this->Ln(0, $cell); $this->x = $parent['startx']; // account for booklet mode if ($this->page > $parent['startpage']) { if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) { $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']); } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) { $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']); } } break; } case 'tablehead': // closing tag used for the thead part $in_table_head = true; case 'table': { // draw borders $table_el = $parent; if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) { $border = 1; } else { $border = 0; } // fix bottom line alignment of last line before page break foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) { // update row-spanned cells if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { if ($trwsp['trid'] == $trkey) { $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1; } if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) { $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey; } } } if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) { $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm']; $dom[$prevtrkey]['endy'] = $pgendy; // update row-spanned cells if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) { $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy; $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1; } } } } $prevtrkey = $trkey; $table_el = $dom[($dom[$key]['parent'])]; } // for each row foreach ($table_el['trids'] as $j => $trkey) { $parent = $dom[$trkey]; // for each cell on the row foreach ($parent['cellpos'] as $k => $cellpos) { if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) { $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx']; $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx']; $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy']; $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage']; $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage']; } else { $endy = $parent['endy']; $startpage = $parent['startpage']; $endpage = $parent['endpage']; } if ($endpage > $startpage) { // design borders around HTML cells. for ($page=$startpage; $page <= $endpage; ++$page) { $this->setPage($page); if ($page == $startpage) { $this->y = $parent['starty']; // put cursor at the beginning of row on the first page $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin(); $cborder = $this->getBorderMode($border, $position='start'); } elseif ($page == $endpage) { $this->y = $this->tMargin; // put cursor at the beginning of last page $ch = $endy - $this->tMargin; $cborder = $this->getBorderMode($border, $position='end'); } else { $this->y = $this->tMargin; // put cursor at the beginning of the current page $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); $cborder = $this->getBorderMode($border, $position='middle'); } if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { $this->SetFillColorArray($cellpos['bgcolor']); $fill = true; } else { $fill = false; } $cw = abs($cellpos['endx'] - $cellpos['startx']); $this->x = $cellpos['startx']; // account for margin changes if ($page > $startpage) { if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) { $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); } } // design a cell around the text $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true); if ($cborder OR $fill) { $pagebuff = $this->getPageBuffer($this->page); $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]); $pend = substr($pagebuff, $this->intmrk[$this->page]); $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); $this->intmrk[$this->page] += strlen($ccode."\n"); } } } else { $this->setPage($startpage); if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { $this->SetFillColorArray($cellpos['bgcolor']); $fill = true; } else { $fill = false; } $this->x = $cellpos['startx']; $this->y = $parent['starty']; $cw = abs($cellpos['endx'] - $cellpos['startx']); $ch = $endy - $parent['starty']; // design a cell around the text $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true); if ($border OR $fill) { if (end($this->transfmrk[$this->page]) !== false) { $pagemarkkey = key($this->transfmrk[$this->page]); $pagemark = &$this->transfmrk[$this->page][$pagemarkkey]; } elseif ($this->InFooter) { $pagemark = &$this->footerpos[$this->page]; } else { $pagemark = &$this->intmrk[$this->page]; } $pagebuff = $this->getPageBuffer($this->page); $pstart = substr($pagebuff, 0, $pagemark); $pend = substr($pagebuff, $pagemark); $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); $pagemark += strlen($ccode."\n"); } } } if (isset($table_el['attribute']['cellspacing'])) { $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px'); $this->y += $cellspacing; } $this->Ln(0, $cell); $this->x = $parent['startx']; if ($endpage > $startpage) { if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) { $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']); } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) { $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']); } } } if (!$in_table_head) { // we are not inside a thead section if (isset($parent['cellpadding'])) { $this->cMargin = $this->oldcMargin; } $this->lasth = $this->FontSize * $this->cell_height_ratio; if (isset($this->theadMargins['top'])) { // restore top margin $this->tMargin = $this->theadMargins['top']; $this->pagedim[$this->page]['tm'] = $this->tMargin; } // reset table header $this->thead = ''; $this->theadMargins = array(); } break; } case 'a': { $this->HREF = ''; break; } case 'sup': { $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k)); break; } case 'sub': { $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k)); break; } case 'div': { $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true); break; } case 'blockquote': { if ($this->rtl) { $this->rMargin -= $this->listindent; } else { $this->lMargin -= $this->listindent; } $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true); break; } case 'p': { $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true); break; } case 'pre': { $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true); $this->premode = false; break; } case 'dl': { --$this->listnum; if ($this->listnum <= 0) { $this->listnum = 0; $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true); } break; } case 'dt': { $this->lispacer = ''; $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true); break; } case 'dd': { $this->lispacer = ''; if ($this->rtl) { $this->rMargin -= $this->listindent; } else { $this->lMargin -= $this->listindent; } $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true); break; } case 'ul': case 'ol': { --$this->listnum; $this->lispacer = ''; if ($this->rtl) { $this->rMargin -= $this->listindent; } else { $this->lMargin -= $this->listindent; } if ($this->listnum <= 0) { $this->listnum = 0; $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true); } $this->lasth = $this->FontSize * $this->cell_height_ratio; break; } case 'li': { $this->lispacer = ''; $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true); break; } case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': { $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true); break; } default : { break; } } $this->tmprtl = false; } /** * Add vertical spaces if needed. * @param int $n number of spaces to add * @param boolean $cell if true add the default cMargin space to each new line (default false). * @param string $h The height of the break. By default, the value equals the height of the last printed cell. * @param boolean $firstorlast if true do not print additional empty lines. * @param string $tag HTML tag to which this space will be applied * @param boolean $closing true if this space will be applied to a closing tag, false otherwise * @access protected */ protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) { if ($firstorlast) { $this->Ln(0, $cell); $this->htmlvspace = 0; return; } if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) { $n = $this->tagvspaces[$tag][intval($closing)]['n']; } if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) { $h = $this->tagvspaces[$tag][intval($closing)]['h']; } if (is_string($h)) { $vsize = $n * $this->lasth; } else { $vsize = $n * $h; } if ($vsize > $this->htmlvspace) { $this->Ln(($vsize - $this->htmlvspace), $cell); $this->htmlvspace = $vsize; } } /** * Set the default bullet to be used as LI bullet symbol * @param string $symbol character or string to be used (legal values are: '' = automatic, '!' = auto bullet, '#' = auto numbering, 'disc', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek') * @access public * @since 4.0.028 (2008-09-26) */ public function setLIsymbol($symbol='!') { $symbol = strtolower($symbol); switch ($symbol) { case '!' : case '#' : case 'disc' : case 'disc' : case 'circle' : case 'square' : case '1': case 'decimal': case 'decimal-leading-zero': case 'i': case 'lower-roman': case 'I': case 'upper-roman': case 'a': case 'lower-alpha': case 'lower-latin': case 'A': case 'upper-alpha': case 'upper-latin': case 'lower-greek': { $this->lisymbol = $symbol; break; } default : { $this->lisymbol = ''; } } } /** * Set the booklet mode for double-sided pages. * @param boolean $booklet true set the booklet mode on, fals eotherwise. * @param float $inner Inner page margin. * @param float $outer Outer page margin. * @access public * @since 4.2.000 (2008-10-29) */ public function SetBooklet($booklet=true, $inner=-1, $outer=-1) { $this->booklet = $booklet; if ($inner >= 0) { $this->lMargin = $inner; } if ($outer >= 0) { $this->rMargin = $outer; } } /** * Swap the left and right margins. * @param boolean $reverse if true swap left and right margins. * @access protected * @since 4.2.000 (2008-10-29) */ protected function swapMargins($reverse=true) { if ($reverse) { // swap left and right margins $mtemp = $this->original_lMargin; $this->original_lMargin = $this->original_rMargin; $this->original_rMargin = $mtemp; $deltam = $this->original_lMargin - $this->original_rMargin; $this->lMargin += $deltam; $this->rMargin -= $deltam; } } /** * Set the vertical spaces for HTML tags. * The array must have the following structure (example): * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1))); * The first array level contains the tag names, * the second level contains 0 for opening tags or 1 for closing tags, * the third level contains the vertical space unit (h) and the number spaces to add (n). * If the h parameter is not specified, default values are used. * @param array $tagvs array of tags and relative vertical spaces. * @access public * @since 4.2.001 (2008-10-30) */ public function setHtmlVSpace($tagvs) { $this->tagvspaces = $tagvs; } /** * Set custom width for list indentation. * @param float $width width of the indentation. Use negative value to disable it. * @access public * @since 4.2.007 (2008-11-12) */ public function setListIndentWidth($width) { return $this->customlistindent = floatval($width); } /** * Set the top/bottom cell sides to be open or closed when the cell cross the page. * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page. * @access public * @since 4.2.010 (2008-11-14) */ public function setOpenCell($isopen) { $this->opencell = $isopen; } /** * Set the color and font style for HTML links. * @param array $color RGB array of colors * @param string $fontstyle additional font styles to add * @access public * @since 4.4.003 (2008-12-09) */ public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') { $this->htmlLinkColorArray = $color; $this->htmlLinkFontStyle = $fontstyle; } /** * convert html string containing value and unit of measure to user's units or points. * @param string $htmlval string containing values and unit * @param string $refsize reference value in points * @param string $defaultunit default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). * @param boolean $point if true returns points, otherwise returns value in user's units * @return float value in user's unit or point if $points=true * @access public * @since 4.4.004 (2008-12-10) */ public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) { $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt'); $retval = 0; $value = 0; $unit = 'px'; $k = $this->k; if ($points) { $k = 1; } if (in_array($defaultunit, $supportedunits)) { $unit = $defaultunit; } if (is_numeric($htmlval)) { $value = floatval($htmlval); } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) { $value = floatval($mnum[1]); if (preg_match('/([a-z%]+)/', $htmlval, $munit)) { if (in_array($munit[1], $supportedunits)) { $unit = $munit[1]; } } } switch ($unit) { // percentage case '%': { $retval = (($value * $refsize) / 100); break; } // relative-size case 'em': { $retval = ($value * $refsize); break; } case 'ex': { $retval = $value * ($refsize / 2); break; } // absolute-size case 'in': { $retval = ($value * $this->dpi) / $k; break; } case 'cm': { $retval = ($value / 2.54 * $this->dpi) / $k; break; } case 'mm': { $retval = ($value / 25.4 * $this->dpi) / $k; break; } case 'pc': { // one pica is 12 points $retval = ($value * 12) / $k; break; } case 'pt': { $retval = $value / $k; break; } case 'px': { $retval = $this->pixelsToUnits($value); break; } } return $retval; } /** * Returns the Roman representation of an integer number * @param int number to convert * @return string roman representation of the specified number * @access public * @since 4.4.004 (2008-12-10) */ public function intToRoman($number) { $roman = ''; while ($number >= 1000) { $roman .= 'M'; $number -= 1000; } while ($number >= 900) { $roman .= 'CM'; $number -= 900; } while ($number >= 500) { $roman .= 'D'; $number -= 500; } while ($number >= 400) { $roman .= 'CD'; $number -= 400; } while ($number >= 100) { $roman .= 'C'; $number -= 100; } while ($number >= 90) { $roman .= 'XC'; $number -= 90; } while ($number >= 50) { $roman .= 'L'; $number -= 50; } while ($number >= 40) { $roman .= 'XL'; $number -= 40; } while ($number >= 10) { $roman .= 'X'; $number -= 10; } while ($number >= 9) { $roman .= 'IX'; $number -= 9; } while ($number >= 5) { $roman .= 'V'; $number -= 5; } while ($number >= 4) { $roman .= 'IV'; $number -= 4; } while ($number >= 1) { $roman .= 'I'; --$number; } return $roman; } /** * Output an HTML list bullet or ordered item symbol * @param int $listdepth list nesting level * @param string $listtype type of list * @param float $size current font size * @access protected * @since 4.4.004 (2008-12-10) */ protected function putHtmlListBullet($listdepth, $listtype='', $size=10) { $size /= $this->k; $fill = ''; $color = $this->fgcolor; $width = 0; $textitem = ''; $tmpx = $this->x; $lspace = $this->GetStringWidth(' '); if ($listtype == '!') { // set default list type for unordered list $deftypes = array('disc', 'circle', 'square'); $listtype = $deftypes[($listdepth - 1) % 3]; } elseif ($listtype == '#') { // set default list type for ordered list $listtype = 'decimal'; } switch ($listtype) { // unordered types case 'none': { break; } case 'disc': { $fill = 'F'; } case 'circle': { $fill .= 'D'; $r = $size / 6; $lspace += (2 * $r); if ($this->rtl) { $this->x = $this->w - $this->x - $lspace; } else { $this->x -= $lspace; } $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8); break; } case 'square': { $l = $size / 3; $lspace += $l; if ($this->rtl) { $this->x = $this->w - $this->x - $lspace; } else { $this->x -= $lspace; } $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color); break; } // ordered types // $this->listcount[$this->listnum]; // $textitem case '1': case 'decimal': { $textitem = $this->listcount[$this->listnum]; break; } case 'decimal-leading-zero': { $textitem = sprintf("%02d", $this->listcount[$this->listnum]); break; } case 'i': case 'lower-roman': { $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum])); break; } case 'I': case 'upper-roman': { $textitem = $this->intToRoman($this->listcount[$this->listnum]); break; } case 'a': case 'lower-alpha': case 'lower-latin': { $textitem = chr(97 + $this->listcount[$this->listnum] - 1); break; } case 'A': case 'upper-alpha': case 'upper-latin': { $textitem = chr(65 + $this->listcount[$this->listnum] - 1); break; } case 'lower-greek': { $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1); break; } /* // Types to be implemented (special handling) case 'hebrew': { break; } case 'armenian': { break; } case 'georgian': { break; } case 'cjk-ideographic': { break; } case 'hiragana': { break; } case 'katakana': { break; } case 'hiragana-iroha': { break; } case 'katakana-iroha': { break; } */ default: { $textitem = $this->listcount[$this->listnum]; } } if (!$this->empty_string($textitem)) { // print ordered item if ($this->rtl) { $textitem = '.'.$textitem; } else { $textitem = $textitem.'.'; } $lspace += $this->GetStringWidth($textitem); if ($this->rtl) { $this->x += $lspace; } else { $this->x -= $lspace; } $this->Write($this->lasth, $textitem, '', false, '', false, 0, false); } $this->x = $tmpx; $this->lispacer = ''; } /** * Returns current graphic variables as array. * @return array graphic variables * @access protected * @since 4.2.010 (2008-11-14) */ protected function getGraphicVars() { $grapvars = array( 'FontFamily' => $this->FontFamily, 'FontStyle' => $this->FontStyle, 'FontSizePt' => $this->FontSizePt, 'rMargin' => $this->rMargin, 'lMargin' => $this->lMargin, 'cMargin' => $this->cMargin, 'LineWidth' => $this->LineWidth, 'linestyleWidth' => $this->linestyleWidth, 'linestyleCap' => $this->linestyleCap, 'linestyleJoin' => $this->linestyleJoin, 'linestyleDash' => $this->linestyleDash, 'DrawColor' => $this->DrawColor, 'FillColor' => $this->FillColor, 'TextColor' => $this->TextColor, 'ColorFlag' => $this->ColorFlag, 'bgcolor' => $this->bgcolor, 'fgcolor' => $this->fgcolor, 'htmlvspace' => $this->htmlvspace, 'lasth' => $this->lasth ); return $grapvars; } /** * Set graphic variables. * @param $gvars array graphic variables * @access protected * @since 4.2.010 (2008-11-14) */ protected function setGraphicVars($gvars) { $this->FontFamily = $gvars['FontFamily']; $this->FontStyle = $gvars['FontStyle']; $this->FontSizePt = $gvars['FontSizePt']; $this->rMargin = $gvars['rMargin']; $this->lMargin = $gvars['lMargin']; $this->cMargin = $gvars['cMargin']; $this->LineWidth = $gvars['LineWidth']; $this->linestyleWidth = $gvars['linestyleWidth']; $this->linestyleCap = $gvars['linestyleCap']; $this->linestyleJoin = $gvars['linestyleJoin']; $this->linestyleDash = $gvars['linestyleDash']; $this->DrawColor = $gvars['DrawColor']; $this->FillColor = $gvars['FillColor']; $this->TextColor = $gvars['TextColor']; $this->ColorFlag = $gvars['ColorFlag']; $this->bgcolor = $gvars['bgcolor']; $this->fgcolor = $gvars['fgcolor']; $this->htmlvspace = $gvars['htmlvspace']; //$this->lasth = $gvars['lasth']; $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.''); if (!$this->empty_string($this->FontFamily)) { $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); } } /** * Returns a temporary filename for caching object on filesystem. * @param string $prefix prefix to add to filename * return string filename. * @access protected * @since 4.5.000 (2008-12-31) */ protected function getObjFilename($name) { return tempnam(K_PATH_CACHE, $name.'_'); } /** * Writes data to a temporary file on filesystem. * @param string $file file name * @param mixed $data data to write on file * @param boolean $append if true append data, false replace. * @access protected * @since 4.5.000 (2008-12-31) */ protected function writeDiskCache($filename, $data, $append=false) { if ($append) { $fmode = 'ab+'; } else { $fmode = 'wb+'; } $f = @fopen($filename, $fmode); if (!$f) { $this->Error('Unable to write cache file: '.$filename); } else { fwrite($f, $data); fclose($f); } // update file lenght (needed for transactions) if (!isset($this->cache_file_lenght['_'.$filename])) { $this->cache_file_lenght['_'.$filename] = strlen($data); } else { $this->cache_file_lenght['_'.$filename] += strlen($data); } } /** * Read data from a temporary file on filesystem. * @param string $file file name * @return mixed retrieved data * @access protected * @since 4.5.000 (2008-12-31) */ protected function readDiskCache($filename) { return file_get_contents($filename); } /** * Set buffer content (always append data). * @param string $data data * @access protected * @since 4.5.000 (2009-01-02) */ protected function setBuffer($data) { $this->bufferlen += strlen($data); if ($this->diskcache) { if (!isset($this->buffer) OR $this->empty_string($this->buffer)) { $this->buffer = $this->getObjFilename('buffer'); } $this->writeDiskCache($this->buffer, $data, true); } else { $this->buffer .= $data; } } /** * Get buffer content. * @return string buffer content * @access protected * @since 4.5.000 (2009-01-02) */ protected function getBuffer() { if ($this->diskcache) { return $this->readDiskCache($this->buffer); } else { return $this->buffer; } } /** * Set page buffer content. * @param int $page page number * @param string $data page data * @param boolean $append if true append data, false replace. * @access protected * @since 4.5.000 (2008-12-31) */ protected function setPageBuffer($page, $data, $append=false) { if ($this->diskcache) { if (!isset($this->pages[$page])) { $this->pages[$page] = $this->getObjFilename('page'.$page); } $this->writeDiskCache($this->pages[$page], $data, $append); } else { if ($append) { $this->pages[$page] .= $data; } else { $this->pages[$page] = $data; } } if ($append AND isset($this->pagelen[$page])) { $this->pagelen[$page] += strlen($data); } else { $this->pagelen[$page] = strlen($data); } } /** * Get page buffer content. * @param int $page page number * @return string page buffer content or false in case of error * @access protected * @since 4.5.000 (2008-12-31) */ protected function getPageBuffer($page) { if ($this->diskcache) { return $this->readDiskCache($this->pages[$page]); } elseif (isset($this->pages[$page])) { return $this->pages[$page]; } return false; } /** * Set image buffer content. * @param string $image image key * @param array $data image data * @access protected * @since 4.5.000 (2008-12-31) */ protected function setImageBuffer($image, $data) { if ($this->diskcache) { if (!isset($this->images[$image])) { $this->images[$image] = $this->getObjFilename('image'.$image); } $this->writeDiskCache($this->images[$image], serialize($data)); } else { $this->images[$image] = $data; } if (!in_array($image, $this->imagekeys)) { $this->imagekeys[] = $image; } ++$this->numimages; } /** * Set image buffer content. * @param string $image image key * @param string $key image sub-key * @param array $data image data * @access protected * @since 4.5.000 (2008-12-31) */ protected function setImageSubBuffer($image, $key, $data) { if (!isset($this->images[$image])) { $this->setImageBuffer($image, array()); } if ($this->diskcache) { $tmpimg = $this->getImageBuffer($image); $tmpimg[$key] = $data; $this->writeDiskCache($this->images[$image], serialize($tmpimg)); } else { $this->images[$image][$key] = $data; } } /** * Get page buffer content. * @param string $image image key * @return string image buffer content or false in case of error * @access protected * @since 4.5.000 (2008-12-31) */ protected function getImageBuffer($image) { if ($this->diskcache AND isset($this->images[$image])) { return unserialize($this->readDiskCache($this->images[$image])); } elseif (isset($this->images[$image])) { return $this->images[$image]; } return false; } /** * Set font buffer content. * @param string $font font key * @param array $data font data * @access protected * @since 4.5.000 (2009-01-02) */ protected function setFontBuffer($font, $data) { if ($this->diskcache) { if (!isset($this->fonts[$font])) { $this->fonts[$font] = $this->getObjFilename('font'); } $this->writeDiskCache($this->fonts[$font], serialize($data)); } else { $this->fonts[$font] = $data; } if (!in_array($font, $this->fontkeys)) { $this->fontkeys[] = $font; } } /** * Set font buffer content. * @param string $font font key * @param string $key font sub-key * @param array $data font data * @access protected * @since 4.5.000 (2009-01-02) */ protected function setFontSubBuffer($font, $key, $data) { if (!isset($this->fonts[$font])) { $this->setFontBuffer($font, array()); } if ($this->diskcache) { $tmpfont = $this->getFontBuffer($font); $tmpfont[$key] = $data; $this->writeDiskCache($this->fonts[$font], serialize($tmpfont)); } else { $this->fonts[$font][$key] = $data; } } /** * Get font buffer content. * @param string $font font key * @return string font buffer content or false in case of error * @access protected * @since 4.5.000 (2009-01-02) */ protected function getFontBuffer($font) { if ($this->diskcache AND isset($this->fonts[$font])) { return unserialize($this->readDiskCache($this->fonts[$font])); } elseif (isset($this->fonts[$font])) { return $this->fonts[$font]; } return false; } /** * Move a page to a previous position. * @param int $frompage number of the source page * @param int $topage number of the destination page (must be less than $frompage) * @return true in case of success, false in case of error. * @access public * @since 4.5.000 (2009-01-02) */ public function movePage($frompage, $topage) { if (($frompage > $this->numpages) OR ($frompage <= $topage)) { return false; } if ($frompage == $this->page) { // close the page before moving it $this->endPage(); } // move all page-related states $tmppage = $this->pages[$frompage]; $tmppagedim = $this->pagedim[$frompage]; $tmppagelen = $this->pagelen[$frompage]; $tmpintmrk = $this->intmrk[$frompage]; if (isset($this->footerpos[$frompage])) { $tmpfooterpos = $this->footerpos[$frompage]; } if (isset($this->footerlen[$frompage])) { $tmpfooterlen = $this->footerlen[$frompage]; } if (isset($this->transfmrk[$frompage])) { $tmptransfmrk = $this->transfmrk[$frompage]; } if (isset($this->PageAnnots[$frompage])) { $tmpannots = $this->PageAnnots[$frompage]; } if (isset($this->newpagegroup[$frompage])) { $tmpnewpagegroup = $this->newpagegroup[$frompage]; } for ($i = $frompage; $i > $topage; --$i) { $j = $i - 1; // shift pages down $this->pages[$i] = $this->pages[$j]; $this->pagedim[$i] = $this->pagedim[$j]; $this->pagelen[$i] = $this->pagelen[$j]; $this->intmrk[$i] = $this->intmrk[$j]; if (isset($this->footerpos[$j])) { $this->footerpos[$i] = $this->footerpos[$j]; } elseif (isset($this->footerpos[$i])) { unset($this->footerpos[$i]); } if (isset($this->footerlen[$j])) { $this->footerlen[$i] = $this->footerlen[$j]; } elseif (isset($this->footerlen[$i])) { unset($this->footerlen[$i]); } if (isset($this->transfmrk[$j])) { $this->transfmrk[$i] = $this->transfmrk[$j]; } elseif (isset($this->transfmrk[$i])) { unset($this->transfmrk[$i]); } if (isset($this->PageAnnots[$j])) { $this->PageAnnots[$i] = $this->PageAnnots[$j]; } elseif (isset($this->PageAnnots[$i])) { unset($this->PageAnnots[$i]); } if (isset($this->newpagegroup[$j])) { $this->newpagegroup[$i] = $this->newpagegroup[$j]; } elseif (isset($this->newpagegroup[$i])) { unset($this->newpagegroup[$i]); } } $this->pages[$topage] = $tmppage; $this->pagedim[$topage] = $tmppagedim; $this->pagelen[$topage] = $tmppagelen; $this->intmrk[$topage] = $tmpintmrk; if (isset($tmpfooterpos)) { $this->footerpos[$topage] = $tmpfooterpos; } elseif (isset($this->footerpos[$topage])) { unset($this->footerpos[$topage]); } if (isset($tmpfooterlen)) { $this->footerlen[$topage] = $tmpfooterlen; } elseif (isset($this->footerlen[$topage])) { unset($this->footerlen[$topage]); } if (isset($tmptransfmrk)) { $this->transfmrk[$topage] = $tmptransfmrk; } elseif (isset($this->transfmrk[$topage])) { unset($this->transfmrk[$topage]); } if (isset($tmpannots)) { $this->PageAnnots[$topage] = $tmpannots; } elseif (isset($this->PageAnnots[$topage])) { unset($this->PageAnnots[$topage]); } if (isset($tmpnewpagegroup)) { $this->newpagegroup[$topage] = $tmpnewpagegroup; } elseif (isset($this->newpagegroup[$topage])) { unset($this->newpagegroup[$topage]); } // adjust outlines $tmpoutlines = $this->outlines; foreach ($tmpoutlines as $key => $outline) { if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) { $this->outlines[$key]['p'] = $outline['p'] + 1; } elseif ($outline['p'] == $frompage) { $this->outlines[$key]['p'] = $topage; } } // adjust links $tmplinks = $this->links; foreach ($tmplinks as $key => $link) { if (($link[0] >= $topage) AND ($link[0] < $frompage)) { $this->links[$key][0] = $link[0] + 1; } elseif ($link[0] == $frompage) { $this->links[$key][0] = $topage; } } // adjust javascript $tmpjavascript = $this->javascript; global $jfrompage, $jtopage; $jfrompage = $frompage; $jtopage = $topage; $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', create_function('$matches', 'global $jfrompage, $jtopage; $pagenum = intval($matches[3]) + 1; if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) { $newpage = ($pagenum + 1); } elseif ($pagenum == $jfrompage) { $newpage = $jtopage; } else { $newpage = $pagenum; } --$newpage; return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); // return to last page $this->lastPage(true); return true; } /** * Remove the specified page. * @param int $page page to remove * @return true in case of success, false in case of error. * @access public * @since 4.6.004 (2009-04-23) */ public function deletePage($page) { if ($page > $this->numpages) { return false; } // delete current page unset($this->pages[$page]); unset($this->pagedim[$page]); unset($this->pagelen[$page]); unset($this->intmrk[$page]); if (isset($this->footerpos[$page])) { unset($this->footerpos[$page]); } if (isset($this->footerlen[$page])) { unset($this->footerlen[$page]); } if (isset($this->transfmrk[$page])) { unset($this->transfmrk[$page]); } if (isset($this->PageAnnots[$page])) { unset($this->PageAnnots[$page]); } if (isset($this->newpagegroup[$page])) { unset($this->newpagegroup[$page]); } if (isset($this->pageopen[$page])) { unset($this->pageopen[$page]); } // update remaining pages for ($i = $page; $i < $this->numpages; ++$i) { $j = $i + 1; // shift pages $this->pages[$i] = $this->pages[$j]; $this->pagedim[$i] = $this->pagedim[$j]; $this->pagelen[$i] = $this->pagelen[$j]; $this->intmrk[$i] = $this->intmrk[$j]; if (isset($this->footerpos[$j])) { $this->footerpos[$i] = $this->footerpos[$j]; } elseif (isset($this->footerpos[$i])) { unset($this->footerpos[$i]); } if (isset($this->footerlen[$j])) { $this->footerlen[$i] = $this->footerlen[$j]; } elseif (isset($this->footerlen[$i])) { unset($this->footerlen[$i]); } if (isset($this->transfmrk[$j])) { $this->transfmrk[$i] = $this->transfmrk[$j]; } elseif (isset($this->transfmrk[$i])) { unset($this->transfmrk[$i]); } if (isset($this->PageAnnots[$j])) { $this->PageAnnots[$i] = $this->PageAnnots[$j]; } elseif (isset($this->PageAnnots[$i])) { unset($this->PageAnnots[$i]); } if (isset($this->newpagegroup[$j])) { $this->newpagegroup[$i] = $this->newpagegroup[$j]; } elseif (isset($this->newpagegroup[$i])) { unset($this->newpagegroup[$i]); } if (isset($this->pageopen[$j])) { $this->pageopen[$i] = $this->pageopen[$j]; } elseif (isset($this->pageopen[$i])) { unset($this->pageopen[$i]); } } // remove last page unset($this->pages[$this->numpages]); unset($this->pagedim[$this->numpages]); unset($this->pagelen[$this->numpages]); unset($this->intmrk[$this->numpages]); if (isset($this->footerpos[$this->numpages])) { unset($this->footerpos[$this->numpages]); } if (isset($this->footerlen[$this->numpages])) { unset($this->footerlen[$this->numpages]); } if (isset($this->transfmrk[$this->numpages])) { unset($this->transfmrk[$this->numpages]); } if (isset($this->PageAnnots[$this->numpages])) { unset($this->PageAnnots[$this->numpages]); } if (isset($this->newpagegroup[$this->numpages])) { unset($this->newpagegroup[$this->numpages]); } if (isset($this->pageopen[$this->numpages])) { unset($this->pageopen[$this->numpages]); } --$this->numpages; $this->page = $this->numpages; // adjust outlines $tmpoutlines = $this->outlines; foreach ($tmpoutlines as $key => $outline) { if ($outline['p'] > $page) { $this->outlines[$key]['p'] = $outline['p'] - 1; } elseif ($outline['p'] == $page) { unset($this->outlines[$key]); } } // adjust links $tmplinks = $this->links; foreach ($tmplinks as $key => $link) { if ($link[0] > $page) { $this->links[$key][0] = $link[0] - 1; } elseif ($link[0] == $page) { unset($this->links[$key]); } } // adjust javascript $tmpjavascript = $this->javascript; global $jpage; $jpage = $page; $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', create_function('$matches', 'global $jpage; $pagenum = intval($matches[3]) + 1; if ($pagenum >= $jpage) { $newpage = ($pagenum - 1); } elseif ($pagenum == $jpage) { $newpage = 1; } else { $newpage = $pagenum; } --$newpage; return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); // return to last page $this->lastPage(true); return true; } /** * Output a Table of Content Index (TOC). * You can override this method to achieve different styles. * @param int $page page number where this TOC should be inserted (leave empty for current page). * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment). * @param string $filler string used to fill the space between text and page number. * @access public * @author Nicola Asuni * @since 4.5.000 (2009-01-02) */ public function addTOC($page='', $numbersfont='', $filler='.') { $fontsize = $this->FontSizePt; $fontfamily = $this->FontFamily; $fontstyle = $this->FontStyle; $w = $this->w - $this->lMargin - $this->rMargin; $spacer = $this->GetStringWidth(' ') * 4; $page_first = $this->getPage(); $lmargin = $this->lMargin; $rmargin = $this->rMargin; $x_start = $this->GetX(); if ($this->empty_string($numbersfont)) { $numbersfont = $this->default_monospaced_font; } if ($this->empty_string($filler)) { $filler = ' '; } if ($this->empty_string($page)) { $gap = ' '; } else { $gap = ''; } foreach ($this->outlines as $key => $outline) { if ($this->rtl) { $aligntext = 'R'; $alignnum = 'L'; } else { $aligntext = 'L'; $alignnum = 'R'; } if ($outline['l'] == 0) { $this->SetFont($fontfamily, $fontstyle.'B', $fontsize); } else { $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']); } $indent = ($spacer * $outline['l']); if ($this->rtl) { $this->rMargin += $indent; $this->x -= $indent; } else { $this->lMargin += $indent; $this->x += $indent; } $link = $this->AddLink(); $this->SetLink($link, 0, $outline['p']); // write the text $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0); $this->SetFont($numbersfont, $fontstyle, $fontsize); if ($this->empty_string($page)) { $pagenum = $outline['p']; } else { // placemark to be replaced with the correct number $pagenum = '{#'.($outline['p']).'}'; if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { $pagenum = '{'.$pagenum.'}'; } } $numwidth = $this->GetStringWidth($pagenum); if ($this->rtl) { $tw = $this->x - $this->lMargin; } else { $tw = $this->w - $this->rMargin - $this->x; } $fw = $tw - $numwidth - $this->GetStringWidth(' '); $numfills = floor($fw / $this->GetStringWidth($filler)); if ($numfills > 0) { $rowfill = str_repeat($filler, $numfills); } else { $rowfill = ''; } if ($this->rtl) { $pagenum = $pagenum.$gap.$rowfill.' '; } else { $pagenum = ' '.$rowfill.$gap.$pagenum; } // write the number //$this->SetX($x_start); $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0); $this->SetX($x_start); $this->lMargin = $lmargin; $this->rMargin = $rmargin; } $page_last = $this->getPage(); $numpages = $page_last - $page_first + 1; if (!$this->empty_string($page)) { for ($p = $page_first; $p <= $page_last; ++$p) { // get page data $temppage = $this->getPageBuffer($p); for ($n = 1; $n <= $this->numpages; ++$n) { // update page numbers $k = '{#'.$n.'}'; $ku = '{'.$k.'}'; $alias_a = $this->_escape($k); $alias_au = $this->_escape('{'.$k.'}'); if ($this->isunicode) { $alias_b = $this->_escape($this->UTF8ToLatin1($k)); $alias_bu = $this->_escape($this->UTF8ToLatin1($ku)); $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl)); $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl)); } if ($n >= $page) { $np = $n + $numpages; } else { $np = $n; } $ns = $this->formatTOCPageNumber($np); $nu = $ns; $sdiff = strlen($k) - strlen($ns) - 1; $sdiffu = strlen($ku) - strlen($ns) - 1; $sfill = str_repeat($filler, $sdiff); $sfillu = str_repeat($filler, $sdiffu); if ($this->rtl) { $ns = $ns.' '.$sfill; $nu = $nu.' '.$sfillu; } else { $ns = $sfill.' '.$ns; $nu = $sfillu.' '.$nu; } $nu = $this->UTF8ToUTF16BE($nu, false); $temppage = str_replace($alias_au, $nu, $temppage); if ($this->isunicode) { $temppage = str_replace($alias_bu, $nu, $temppage); $temppage = str_replace($alias_cu, $nu, $temppage); $temppage = str_replace($alias_b, $ns, $temppage); $temppage = str_replace($alias_c, $ns, $temppage); } $temppage = str_replace($alias_a, $ns, $temppage); } // save changes $this->setPageBuffer($p, $temppage); } // move pages for ($i = 0; $i < $numpages; ++$i) { $this->movePage($page_last, $page); } } $this->SetFont($fontfamily, $fontstyle, $fontsize); } /** * Stores a copy of the current TCPDF object used for undo operation. * @access public * @since 4.5.029 (2009-03-19) */ public function startTransaction() { if (isset($this->objcopy)) { // remove previous copy $this->commitTransaction(); } // clone current object $this->objcopy = $this->objclone($this); } /** * Delete the copy of the current TCPDF object used for undo operation. * @access public * @since 4.5.029 (2009-03-19) */ public function commitTransaction() { if (isset($this->objcopy)) { $this->objcopy->_destroy(true, true); unset($this->objcopy); } } /** * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction(). * @return TCPDF object. * @access public * @since 4.5.029 (2009-03-19) */ public function rollbackTransaction() { if (isset($this->objcopy)) { if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) { // truncate files to previous values foreach ($this->objcopy->cache_file_lenght as $file => $lenght) { $file = substr($file, 1); $handle = fopen($file, 'r+'); ftruncate($handle, $lenght); } } $this->_destroy(true, true); return $this->objcopy; } return $this; } /** * Creates a copy of a class object * @param object $object class object to be cloned * @return cloned object * @access public * @since 4.5.029 (2009-03-19) */ public function objclone($object) { return @clone($object); } /** * Determine whether a string is empty. * @param srting $str string to be checked * @return boolean true if string is empty * @access public * @since 4.5.044 (2009-04-16) */ public function empty_string($str) { return (is_null($str) OR (is_string($str) AND (strlen($str) == 0))); } } // END OF TCPDF CLASS } //============================================================+ // END OF FILE //============================================================+ ?>
- L : left align
- C : center
- R : right align
- '' : empty string : left for LTR or right for RTL