From f0f668b5ce8ec34eb7697e7fd881f080a68e9bc5 Mon Sep 17 00:00:00 2001
From: YuCheng Hu
+ * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.
+ * TCPDF main features are:
+ *
+ *
+ * Tools to encode your unicode fonts are on fonts/utils directory.
+ * 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 (!$this->isunicode) { + // split string into array of equivalent codes + $strarr = array(); + $strlen = strlen($str); + for($i=0; $i < $strlen; $i++) { + $strarr[] = ord($str{$i}); + } + 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; + } + } + 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 + * @since 1.1 + */ + public function setHeaderFont($font) { + $this->header_font = $font; + } + + /** + * Get header font. + * @return array() + * @since 4.0.012 (2008-07-24) + */ + public function getHeaderFont() { + return $this->header_font; + } + + /** + * Set footer font. + * @param array $font font + * @since 1.1 + */ + public function setFooterFont($font) { + $this->footer_font = $font; + } + + /** + * Get Footer font. + * @return array() + * @since 4.0.012 (2008-07-24) + */ + public function getFooterFont() { + return $this->footer_font; + } + + /** + * Set language array. + * @param array $language + * @since 1.1 + */ + public function setLanguageArray($language) { + $this->l = $language; + $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; + } + + /** + * Returns the PDF data. + */ + 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. + * @return the number of cells used or the remaining text if $firstline = true; + * @access public + */ + public function addHtmlLink($url, $name, $fill=0, $firstline=false) { + if ($url{0} == '#') { + // convert url to internal link + $page = intval(substr($url, 1)); + $url = $this->AddLink(); + $this->SetLink($url, 0, $page); + } + $prevcolor = $this->fgcolor; + $this->SetTextColor(0, 0, 255); + $prevstyle = $this->FontStyle; + $this->SetFont('', $this->FontStyle.'U'); + $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline); + $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 + * @access protected + */ + protected function convertHTMLColorToDec($color='#000000') { + global $webcolor; + $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces + // set default color to be returned in case of error + $returncolor = array ('R' => 0, 'G' => 0, 'B' => 0); + if (empty($color)) { + return $returncolor; + } + if (substr(strtolower($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[strtolower($color)])) { + $color_code = $webcolor[strtolower($color)]; + } else { + return $returncolor; + } + } 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 Units. + * @param int $px pixels + * @return float millimeters + * @access public + */ + public function pixelsToUnits($px) { + return $px / $this->k; + } + + /** + * Reverse function for htmlentities. + * Convert entities in UTF-8. + * @param $text_to_convert Text to convert. + * @return string converted + */ + 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 + * @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. + * @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 + * @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 + * @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 + * @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 + * @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 + * @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. + * @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 ----------------------- + // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni + + /** + * 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. + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function StartTransform() { + $this->_out('q'); + } + + /** + * 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. + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function StopTransform() { + $this->_out('Q'); + if (isset($this->transfmatrix)) { + array_pop($this->transfmatrix); + } + } + /** + * 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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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) + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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. + * @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]); + } + + // 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. + * @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)); + $this->_out($this->linestyleWidth); + } + + /** + * Returns the current the line width. + * @return int Line width + * @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('/[\s]*<\/tr>[\s]*/', '', $html); + $html = preg_replace('/[\s]*
'); + //replace carriage returns, newlines and tabs + $repTable = array("\t" => ' ', "\n" => ' ', "\r" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); + $html = strtr($html, $repTable); + // remove extra spaces from tables + $html = preg_replace('/[\s]*<\/table>[\s]*/', '
[\s]*/', '', $html); + $html = preg_replace('/[\s]* [\s]*/', '', $html); + $html = preg_replace('/[\s]* /', ' ', $html); + $html = preg_replace('/<\/table>', $html); + $html = preg_replace('/<\/td>/', ' /', '', $html); + $html = preg_replace('/ ]*)>/xi', '
', $html); + $html = preg_replace('/[\s]*
- [\s]*/', '
', $html); + // pattern for generic tag + $tagpattern = '/(<[^>]+>)/Uu'; + // explodes the string + $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + // count elements + $maxel = count($a); + $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'] = ''; + $key++; + $level = array(); + array_push($level, 0); // root + while ($key <= $maxel) { + if ($key > 0) { + $dom[$key] = array(); + } + $element = $a[($key-1)]; + if (preg_match($tagpattern, $element)) { + // html tag + $dom[$key]['tag'] = true; + $element = substr($element, 1, -1); + // get tag name + preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); + $dom[$key]['value'] = strtolower($tag[1]); + 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']; + // 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[($i-1)]; + } + $key = $i; + } + } 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 + if ($key > 0) { + $dom[$key]['fontname'] = $dom[($dom[$key]['parent'])]['fontname']; + $dom[$key]['fontstyle'] = $dom[($dom[$key]['parent'])]['fontstyle']; + $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize']; + $dom[$key]['bgcolor'] = $dom[($dom[$key]['parent'])]['bgcolor']; + $dom[$key]['fgcolor'] = $dom[($dom[$key]['parent'])]['fgcolor']; + $dom[$key]['align'] = $dom[($dom[$key]['parent'])]['align']; + } + // 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 = split(',', strtolower($dom[$key]['style']['font-family'])); + foreach ($fontslist as $font) { + $font = trim(strtolower($font)); + if (in_array($font, $this->fontlist)) { + $dom[$key]['fontname'] = $font; + break; + } + } + } + } + // font size + if (isset($dom[$key]['style']['font-size'])) { + $fsize = trim($dom[$key]['style']['font-size']); + switch ($fsize) { + 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; + } + default: { + $dom[$key]['fontsize'] = intval($fsize); + } + } + } + // 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 (!empty($dom[$key]['style']['color']))) { + $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']); + } + // background color + if (isset($dom[$key]['style']['background-color']) AND (!empty($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 ($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'] = intval($dom[$key]['style']['width']); + } + // check for height attribute + if (isset($dom[$key]['style']['height'])) { + $dom[$key]['height'] = intval($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 font tag + if ($dom[$key]['value'] == 'font') { + // font family + if (isset($dom[$key]['attribute']['face'])) { + $fontslist = split(',', strtolower($dom[$key]['attribute']['face'])); + foreach ($fontslist as $font) { + $font = trim(strtolower($font)); + if (in_array($font, $this->fontlist)) { + $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']); + } + } + } + if (($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) { + // force natural alignment for lists + 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']{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 + } + 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 (($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 (!empty($dom[$key]['attribute']['color']))) { + $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']); + } + // set background color attribute + if (isset($dom[$key]['attribute']['bgcolor']) AND (!empty($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'] = intval($dom[$key]['attribute']['width']); + } + // check for height attribute + if (isset($dom[$key]['attribute']['height'])) { + $dom[$key]['height'] = intval($dom[$key]['attribute']['height']); + } + // check for text alignment + if (isset($dom[$key]['attribute']['align']) AND (!empty($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); + } + $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, small, span, strong, sub, sup, table, td, th, tr, 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:+ */ + 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'); + 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] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page]; + } else { + $this->footerpos[$this->page] = strlen($this->pages[$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 ((empty($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) + AND (!((($this->y + $this->pixelsToUnits($dom[$key]['attribute']['height'])) > $this->PageBreakTrigger) + AND (!$this->InFooter) + AND $this->AcceptPageBreak())) + ) { + if ($this->page > $startlinepage) { + // fix lines splitted over two pages + if (isset($this->footerlen[$startlinepage])) { + $curpos = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage]; + } + // line to be moved one page forward + $linebeg = substr($this->pages[$startlinepage], $startlinepos, ($curpos - $startlinepos)); + $tstart = substr($this->pages[$startlinepage], 0, $startlinepos); + $tend = substr($this->pages[$startlinepage], $curpos); + // remove line start from previous page + $this->pages[$startlinepage] = $tstart.''.$tend; + $tstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]); + $tend = substr($this->pages[$this->page], $this->intmrk[$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->pages[$this->page] = $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend; + // shift the annotations and links + 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; + } + } + } + } + $this->y += (($curfontsize / $this->k) - $this->pixelsToUnits($dom[$key]['attribute']['height'])); + $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 = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage]; + } + // line to be moved one page forward + $linebeg = substr($this->pages[$startlinepage], $startlinepos, ($curpos - $startlinepos)); + $tstart = substr($this->pages[$startlinepage], 0, $startlinepos); + $tend = substr($this->pages[$startlinepage], $curpos); + // remove line start from previous page + $this->pages[$startlinepage] = $tstart.''.$tend; + $tstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]); + $tend = substr($this->pages[$this->page], $this->intmrk[$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->pages[$this->page] = $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend; + // shift the annotations and links + 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; + } + } + } + } + $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 = strlen($this->pages[$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 (empty($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->pages[$startlinepage], 0, $startlinepos); + if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) { + $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage]; + $midpos = min($opentagpos, $this->footerpos[$startlinepage]); + } elseif (isset($opentagpos)) { + $midpos = $opentagpos; + } elseif (isset($this->footerlen[$startlinepage])) { + $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage]; + $midpos = $this->footerpos[$startlinepage]; + } else { + $midpos = 0; + } + if ($midpos > 0) { + $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos)); + $pend = substr($this->pages[$startlinepage], $midpos); + } else { + $pmid = substr($this->pages[$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; + //foreach ($lnstring[1] as $kk => $value) { + 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 = ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize / $this->k; + $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), ") ".(-2830 * $spacew)." (", $matches[1]).")]";'), $pmidtemp); + $this->pages[$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->pages[$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->pages[$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] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page]; + } else { + $this->footerpos[$this->page] = strlen($this->pages[$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']) { + // 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']; + } + // calculate cell width + if (isset($dom[$trid]['width'])) { + $table_width = $this->pixelsToUnits($dom[$trid]['width']); + } else { + $table_width = $w; + } + if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { + $currentcmargin = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding']); + } else { + $currentcmargin = 0; + } + $this->cMargin = $currentcmargin; + if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) { + $cellspacing = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing']); + } else { + $cellspacing = 0; + } + if ($this->rtl) { + $cellspacingx = -$cellspacing; + } else { + $cellspacingx = $cellspacing; + } + $colspan = $dom[$key]['attribute']['colspan']; + if (isset($dom[$key]['width'])) { + $cellw = $this->pixelsToUnits($dom[$key]['width']); + } else { + $cellw = ($colspan * ($table_width / $dom[$table_el]['cols'])); + } + $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))) { + $this->x = $rsendx + $cellspacingx; + if (($trwsp['rowspan'] == 1) + AND (isset($dom[$trid]['endy'])) + AND (isset($dom[$trid]['endpage'])) + AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) { + $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] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page]; + } else { + $this->footerpos[$this->page] = strlen($this->pages[$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, 0, $cell_content, false, $lalign, false, 2, '', '', true, 0, true); + $this->lasth = $prevLastH; + $this->cMargin = $currentcmargin; + $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'])) { + 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 (isset($this->footerlen[$this->page])) { + $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page]; + } else { + $this->footerpos[$this->page] = strlen($this->pages[$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 (!empty($this->lispacer)) { + $this->SetFont($pfontname, $pfontstyle, $pfontsize); + $this->lasth = $this->FontSize * $this->cell_height_ratio; + $minstartliney = $this->y; + $tmpx = $this->x; + $lspace = $this->GetStringWidth($this->lispacer.' '); + if ($this->rtl) { + $this->x += $lspace; + } else { + $this->x -= $lspace; + } + $this->Write($this->lasth, $this->lispacer, '', false, '', false, 0, false); + $this->x = $tmpx; + $this->lispacer = ''; + $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->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->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; + } + if ($this->HREF) { + // HTML Link + $strrest = $this->addHtmlLink($this->HREF, $dom[$key]['value'], $wfill, true); + } 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->pages[$startlinepage], 0, $startlinepos); + if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) { + $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage]; + $midpos = min($opentagpos, $this->footerpos[$startlinepage]); + } elseif (isset($opentagpos)) { + $midpos = $opentagpos; + } elseif (isset($this->footerlen[$startlinepage])) { + $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage]; + $midpos = $this->footerpos[$startlinepage]; + } else { + $midpos = 0; + } + if ($midpos > 0) { + $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos)); + $pend = substr($this->pages[$startlinepage], $midpos); + } else { + $pmid = substr($this->pages[$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->pages[$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 (isset($tag['attribute']['cellpadding'])) { + $cp = $this->pixelsToUnits($tag['attribute']['cellpadding']); + $this->oldcMargin = $this->cMargin; + $this->cMargin = $cp; + } + if (isset($tag['attribute']['cellspacing'])) { + $cs = $this->pixelsToUnits($tag['attribute']['cellspacing']); + } + $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; + if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) { + $hrWidth = $this->pixelsToUnits($tag['attribute']['width']); + } else { + $hrWidth = $this->w - $this->lMargin - $this->rMargin; + } + $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 = $tag['attribute']['href']; + } + 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'] = 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 (!empty($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(' '); + } + } + + if (($type == 'eps') OR ($type == 'ai')) { + $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', true, $align); + } else { + $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', '', $align); + } + 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->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; + } + break; + } + case 'li': { + $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); + if ($this->listordered[$this->listnum]) { + if (isset($tag['attribute']['value'])) { + $this->listcount[$this->listnum] = intval($tag['attribute']['value']); + } + $this->listcount[$this->listnum]++; + if ($this->rtl) { + $this->lispacer = '.'.($this->listcount[$this->listnum]); + } else { + $this->lispacer = ($this->listcount[$this->listnum]).'.'; + } + } else { + //unordered list symbol + $this->lispacer = $this->lisymbol; + } + 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 '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; + } + 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'))); + //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']; + } + } + } + } + $this->setPage($parent['endpage']); + $this->y = $parent['endy']; + if (isset($dom[$table_el]['attribute']['cellspacing'])) { + $cellspacing = $this->pixelsToUnits($dom[$table_el]['attribute']['cellspacing']); + $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 '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 = $border ? 'LTR' : 0; + } elseif ($page == $endpage) { + $this->y = $this->tMargin; // put cursor at the beginning of last page + $ch = $endy - $this->tMargin; + $cborder = $border ? 'LRB' : 0; + } else { + $this->y = $this->tMargin; // put cursor at the beginning of the current page + $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); + $cborder = $border ? 'LR' : 0; + } + 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']); + } + } + if (!$this->opencell) { + $cborder = $border; + } + // design a cell around the text + $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill); + if ($cborder OR $fill) { + $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]); + $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]); + $this->pages[$this->page] = $pstart.$ccode."\n".$pend; + $this->intmrk[$this->page] += strlen($ccode."\n"); + } + } + } else { + $this->setPage($startpage); + $ch = $endy - $parent['starty']; + 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']; + $this->y = $parent['starty']; + // design a cell around the text + $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill); + if ($border OR $fill) { + $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]); + $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]); + $this->pages[$this->page] = $pstart.$ccode."\n".$pend; + $this->intmrk[$this->page] += strlen($ccode."\n"); + } + } + } + if (isset($table_el['attribute']['cellspacing'])) { + $cellspacing = $this->pixelsToUnits($table_el['attribute']['cellspacing']); + $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 (isset($parent['cellpadding'])) { + $this->cMargin = $this->oldcMargin; + } + $this->lasth = $this->FontSize * $this->cell_height_ratio; + 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 '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 character or string to be used as LI item symbol on UL lists. + * @param string $symbol character or string to be used + * @access public + * @since 4.0.028 (2008-09-26) + */ + public function setLIsymbol($symbol='-') { + $this->lisymbol = $symbol; + } + + /** + * 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; + } + + /** + * 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, + '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->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 (!empty($this->FontFamily)) { + $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); + } + } + } // END OF TCPDF CLASS +} +//============================================================+ +// END OF FILE +//============================================================+ +?>
- L : left align
- C : center
- R : right align
- '' : empty string : left for LTR or right for RTL