* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @version $Id: tcpdf_adapter.cls.php 448 2011-11-13 13:00:03Z fabien.menager $ */ namespace Dompdf\Adapter; //*** use Dompdf\Canvas; use Dompdf\Dompdf; use Dompdf\Helpers; use Dompdf\Exception; use Dompdf\Image\Cache; use Dompdf\PhpEvaluator; /** * TCPDF PDF Rendering interface * * TCPDF_Adapter provides a simple, stateless interface to TCPDF. * * Unless otherwise mentioned, all dimensions are in points (1/72 in). * The coordinate origin is in the top left corner and y values * increase downwards. * * See {@link http://tcpdf.sourceforge.net} for more information on * the underlying TCPDF class. * * @package dompdf */ class TCPDF implements Canvas { //*** /** * Dimensions of paper sizes in points * * @var array; */ static public $PAPER_SIZES = array (); // Set to // CPDF_Adapter::$PAPER_SIZES below. /** * Fudge factor to adjust reported font heights * * CPDF reports larger font heights than TCPDF. This factor * adjusts the height reported by get_font_height(). * * CORRECTION: at a certain point, with given DOMPDF and TCPDF versions, this was true * and the number was 1.116. Now it does not seem necessary anymore. * Just in case it will be useful again in the future, I leave it in the code setting it to 1 * * @var float */ const FONT_HEIGHT_SCALE_NORMAL = 1.116; const FONT_HEIGHT_SCALE_BOLD = 1.153; /** * Instance of the TCPDF class * * @var TCPDF */ private $_pdf; /** * PDF width in points * * @var float */ private $_width; /** * PDF height in points * * @var float */ private $_height; /** * Last fill colour used * * @var array */ private $_last_fill_color; /** * Last stroke colour used * * @var array */ private $_last_stroke_color; /** * Cache of image handles * * @var array */ private $_imgs; /** * Cache of font handles * * @var array */ private $_fonts; /** * List of objects (templates) to add to multiple pages * * @var array */ private $_objs; /** * Text to display on every page * * @var array */ private $_page_text; /** * Array of pages for accesing after rendering is initially complete * * @var array */ private $_pages; /** * Array of temporary cached images to be deleted when processing is complete * * @var array */ private $_image_cache; //// /// TCPDF only /// /// /** * Map named links (internal) to links ID * @var unknown_type */ private $_nameddest; /** * Saves internal links info for later insertion * @var unknown_type */ private $_internal_links; private $_currentLineTransparency; // not used because line transparency is set by fill transparency private $_currentFillTransparency; /** * Class constructor * * @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or * an array(xmin,ymin,xmax,ymax) * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') * @param DOMPDF $dompdf */ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) { //*** if ( is_array($paper) ) $size = $paper; else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) $size = self::$PAPER_SIZES[mb_strtolower($paper)]; else $size = self::$PAPER_SIZES["letter"]; $ori = 'P'; // *** if ( mb_strtolower($orientation) === "landscape" ) { list($size[2], $size[3]) = array($size[3], $size[2]); $ori = 'L'; // *** } $this->_width = $size[2] - $size[0]; $this->_height = $size[3] - $size[1]; $this->_dompdf = $dompdf; //***$this->_pdf = new My_TCPDF('P', 'pt', $paper, true, 'UTF-8', false); $this->_pdf = new My_TCPDF($ori, 'pt', $paper, true, 'UTF-8', false); // *** $this->_pdf->SetCreator("DOMPDF Converter"); // CreationDate and ModDate info are added by TCPDF itself // don't use TCPDF page defaults $this->_pdf->SetAutoPageBreak(false); $this->_pdf->SetMargins(0, 0, 0, true); $this->_pdf->setPrintHeader(false); // remove default header/footer $this->_pdf->setPrintFooter(false); $this->_pdf->setHeaderMargin(0); $this->_pdf->setFooterMargin(0); $this->_pdf->SetCellPadding(0); $this->_pdf->AddPage(); $this->_pdf->SetDisplayMode('fullpage', 'continuous'); $this->_page_number = $this->_page_count = 1; $this->_page_text = array (); $this->_pages = array($this->_pdf->PageNo()); $this->_image_cache = array(); // other TCPDF stuff... $this->_objs = array(); // for templating support $this->_nameddest = array(); // for internal link support $this->_internal_links = array(); // " " " $this->_pdf->setAlpha(1.0); $this->_currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0); $this->_currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0); $this->_last_fill_color = $this->_last_stroke_color = null; //Helpers::dompdf_debug("trace", "Exit"); } function get_dompdf(){ //*** return $this->_dompdf; } /** * Class destructor * * Deletes all temporary image files */ function __destruct() { foreach ($this->_image_cache as $img) { //debugpng if (DEBUGPNG) print '[__destruct unlink '.$img.']'; if (!DEBUGKEEPTEMP) unlink($img); } } /** * Returns the Tcpdf instance * * @return Tcpdf */ function get_lib_obj() { return $this->_pdf; } /** * Add meta information to the PDF * * TCPDF does not have a generic method to do this, but limits the possible info to add * to well specific cases * * @param string $label label of the value (Creator, Producter, etc.) * @param string $value the text to set */ function add_info($label, $value) { global $_dompdf_warnings; switch($label) { case 'Creator': $this->_pdf->SetCreator($value); break; case 'Author': $this->_pdf->SetAuthor($value); break; case 'Title': $this->_pdf->SetTitle($value); break; case 'Subject': $this->_pdf->SetSubject($value); break; case 'Keywords': $this->_pdf->SetKeywords($value); break; default: $_dompdf_warnings[] = "add_info: label '$label' is not supported by the TCPDF library."; break; } } /** * Opens a new 'object' (template in PDFLib-speak) * * While an object is open, all drawing actions are recored in the object, * as opposed to being drawn on the current page. Objects can be added * later to a specific page or to several pages. * * The return value is an integer ID for the new object. * * @see TCPDF_Adapter::close_object() * @see TCPDF_Adapter::add_object() * * @return int */ function open_object() { /* TCPDF does not appear to have template support. Options: * 1) add template support to TCPDF * 2) throw an error * 3) implement the template support in this adapter * * I have chosen a mix of 1st and 3rd options, using code from the PDFLIB and CPDF adapters, and the CPDF library, * to make a TCPDF subclass * The only tests performed are those in the dompdf/www/test directory. * * What it does essentially is saving the TCPDF output buffer in a local stack, and resetting it to its initial value (empty string). * When the template is closed, the output collected from its opening, is moved to an internal template dictionary, * and the TCPDF output buffer is restored to the saved values (from the stack). * * The TCPDF output buffer cannot be accessed because it is private, and its accessor methods are protected. * Therefore, I used a derived class (My_TCPDF - see bottom of this file) * * To prevent any side effects to the TCPDF object state, while a template is open, I issue a rollbackTransaction() when the template is closed, * to restore everything to the original state. I don't know yet if this is necessary, correct, useful, etc. It needs further learning and testing. * It also makes the use of the stack pretty redundant. For the moment I keep both of them. */ //throw new DOMPDF_Exception("TCPDF Adapter does not support opening objects."); $this->_pdf->startTransaction(); // ??? $ret = $this->_pdf->openObject(); $this->_objs[$ret] = array("start_page" => $this->_pdf->PageNo()); return $ret; } /** * Reopen an existing object * * @param int $object the ID of a previously opened object */ function reopen_object($object) { $this->_pdf->startTransaction(); // ??? $this->_pdf->reopenObject($object); } /** * Close the current object * * @see TCPDF_Adapter::open_object() */ function close_object() { $this->_pdf->closeObject(); $this->_pdf->rollbackTransaction(true); // ??? } /** * Adds the specified object to the document * * $where can be one of: * - 'add' add to current page only * - 'all' add to every page from the current one onwards * - 'odd' add to all odd numbered pages from now on * - 'even' add to all even numbered pages from now on * - 'next' add the object to the next page only * - 'nextodd' add to all odd numbered pages from the next one * - 'nexteven' add to all even numbered pages from the next one * * @param int $object the object handle returned by open_object() * @param string $where */ function add_object($object, $where = 'all') { if (mb_strpos($where, "next") !== false) { $this->_objs[$object]["start_page"]++; $where = str_replace("next", "", $where); if ($where == "") { $where = "add"; } } $this->_objs[$object]["where"] = $where; } /** * Stops the specified object from appearing in the document. * * The object will stop being displayed on the page following the * current one. * * @param int $object */ function stop_object($object) { if (! isset ($this->_objs[$object])) { return; } $start = $this->_objs[$object]["start_page"]; $where = $this->_objs[$object]["where"]; // Place the object on this page if required $page_number = $this->_pdf->PageNo(); if ($page_number >= $start && (($page_number % 2 == 0 && $where == "even") || ($page_number % 2 == 1 && $where == "odd") || ($where == "all"))) { $data = $this->_pdf->getObject($object); $this->_pdf->setPageBuffer($page_number, $data['c'], true); } unset ($this->_objs[$object]); } /** * Add all active objects to the current page */ protected function _place_objects() { //Helpers::dompdf_debug("trace", "()"); foreach ($this->_objs as $obj=>$props) { //var_dump($props); $start = $props["start_page"]; $where = $props["where"]; // Place the object on this page if required $page_number = $this->_pdf->PageNo(); if ($page_number >= $start && (($page_number % 2 == 0 && $where == "even") || ($page_number % 2 == 1 && $where == "odd") || ($where == "all"))) { $data = $this->_pdf->getObject($obj); //Helpers::dompdf_debug("trace", "object data = " . $data['c']); $this->_pdf->setPageBuffer($page_number, $data['c'], true); } } } /****************************************************************************** *** Protected methods *** ******************************************************************************/ /** * Sets the stroke color * * @param array $color */ /* protected function _set_stroke_color($color) { //Helpers::dompdf_debug("trace", "([$color[0], $color[1], $color[2]])"); //if ($this->_last_stroke_color == $color) { // return; //} $this->_last_stroke_color = $color; list ($r, $g, $b) = $this->_get_rgb($color); $this->_pdf->SetDrawColor($r, $g, $b); } */ /** * Sets the fill color * * @param array $color */ /* protected function _set_fill_color($color) { //Helpers::dompdf_debug("trace", "([$color[0], $color[1], $color[2]])"); if ($this->_last_fill_color == $color) { return; } $this->_last_fill_color = $color; list ($r, $g, $b) = $this->_get_rgb($color); $this->_pdf->SetFillColor($r, $g, $b); } */ /** * Sets line transparency * @see Tcpdf::setAlpha() * * In TCFTP the setAlpha() method, sets both line transparency and fill transparency * * Valid blend modes are (case-sensitive): * * Normal, Multiply, Screen, Overlay, Darken, Lighten, * ColorDodge, ColorBurn, HardLight, SoftLight, Difference, * Exclusion * * @param string $mode the blending mode to use * @param float $opacity 0.0 fully transparent, 1.0 fully opaque */ protected function _set_line_transparency($mode, $opacity) { //Helpers::dompdf_debug("trace", "($mode, $opacity)"); // Only create a new graphics state if required //if ( $mode != $this->_currentFillTransparency["mode"] || //$opacity != $this->_currentFillTransparency["opacity"] ) { $this->_pdf->setAlpha($opacity, $mode); $this->_currentFillTransparency["opacity"] = $opacity; $this->_currentFillTransparency["mode"] = $mode; //} } /** * Sets fill transparency * @see Tcpdf::setAlpha() * * In TCFTP the setAlpha() method, sets both line transparency and fill transparency * * Valid blend modes are (case-sensitive): * * Normal, Multiply, Screen, Overlay, Darken, Lighten, * ColorDogde, ColorBurn, HardLight, SoftLight, Difference, * Exclusion * * @param string $mode the blending mode to use * @param float $opacity 0.0 fully transparent, 1.0 fully opaque */ protected function _set_fill_transparency($mode, $opacity) { //Helpers::dompdf_debug("trace", "($mode, $opacity)"); // Only create a new graphics state if required //if ( $mode != $this->_currentFillTransparency["mode"] || //$opacity != $this->_currentFillTransparency["opacity"] ) { $this->_pdf->setAlpha($opacity, $mode); $this->_currentFillTransparency["opacity"] = $opacity; $this->_currentFillTransparency["mode"] = $mode; //} } /** * Sets the line style * * @see TCpdf::setLineStyle() * * @param float width * @param string cap * @param string join * @param array dash */ /* protected function _set_line_style($width, $cap, $join, $dash) { //Helpers::dompdf_debug("trace", "($width, $cap, $join, $dash)"); //var_dump($dash); if ($dash) { $dash = implode(',', $dash); } else { $dash = 0; } $style = array ('width'=>$width, 'cap'=>$cap, 'join'=>$join, 'dash'=>$dash); $this->_pdf->SetLineStyle($style); } */ /** * * @param unknown_type $color * @return unknown_type */ protected function _get_rgb($color) { return array (round(255*$color[0]), round(255*$color[1]), round(255*$color[2])); } /** * * @param unknown_type $color * @param unknown_type $width * @param unknown_type $cap * @param unknown_type $join * @param unknown_type $dash * @return unknown_type */ protected function _make_line_style($color = '', $width='', $cap='', $join='', $dash='') { //Helpers::dompdf_debug("trace", "($color, $width, $cap, $join, $dash)"); $style = array(); if ($color) { $style['color'] = $this->_get_rgb($color); } if ($width) { $style['width'] = $width; } if ($cap) { $style['cap'] = $cap; } if ($join) { $style['join'] = $join; } $style['dash'] = $dash ? implode(',', $dash) : 0; return $style; } /** * Convert a GIF image to a PNG image * * @return string The url of the newly converted image */ protected function _convert_gif_to_png($image_url) { if ( !function_exists("imagecreatefromgif") ) { throw new DOMPDF_Exception("Function imagecreatefromgif() not found. Cannot convert gif image: $image_url. Please install the image PHP extension."); } $old_err = set_error_handler("record_warnings"); $tmp_img = getImageSize($img_url); if ($tmp_img[2] == '1') { $img = imagecreatefromgif($img_url); if ($img) { imageinterlace($img, 0); $tmp_filename = tempnam(DOMPDF_TEMP_DIR, "dompdf_img_"); imagepng($img, $tmp_filename); rename($tmp_filename, $tmp_filename.'.png'); $img_url = $tmp_filename.'.png'; } else { $img_url = "../../lib/res/broken_image.png"; } } restore_error_handler(); $this->_image_cache[] = $img_url; return $img_url; } /** * Add text to each page after rendering is complete */ protected function _add_page_text() { //Helpers::dompdf_debug("trace", "()"); if (!count($this->_page_text)) { return; } $page_number = 1; $eval = null; foreach ($this->_pages as $pid) { //$this->reopen_object($pid); $this->_pdf->setPage($pid); foreach ($this->_page_text as $pt) { extract($pt); switch($_t) { case "text": $text = str_replace( array ("{PAGE_NUM}", "{PAGE_COUNT}"), array ($page_number, $this->_pdf->getNumPages()), $text); $this->text($x, $y, $text, $font, $size, $color, $adjust, 0, $angle); //*** break; case "script": if (!$eval) { $eval = new PHP_Evaluator($this); } $eval->evaluate($code, array ('PAGE_NUM'=>$page_number, 'PAGE_COUNT'=>$this->_pdf->getNumPages())); break; } } //$this->close_object(); $page_number++; } } /** * * @return unknown_type */ protected function _add_internal_links() { //Helpers::dompdf_debug("trace", "()"); if (!count($this->_internal_links)) { return; } foreach ($this->_internal_links as $link) { extract($link); // compact('page', 'name', 'x', 'y', 'width', 'height') $this->_pdf->setPage($page); $this->_pdf->Link($x, $y, $width, $height, $this->_nameddest[$name]); } } /** * * @param unknown_type $font * @return unknown_type */ protected function _get_font($font) { $name = basename($font); $a = explode('-', $name); $f = strtolower($a[0]); $s = ''; //*** if (strpos($a[1], 'Bold') !== false) { // $s .= 'B'; // } // if (strpos($a[1], 'Italic') !== false) { // $s .= 'I'; // } //Helpers::dompdf_debug("trace2", "($font): returns $f, $s"); return array('family' => $f, 'style' => $s); } /****************************************************************************** *** Interface Canvas implementation *** ******************************************************************************/ /** * @return Number */ function get_width() { //Helpers::dompdf_debug("trace", ": returns $this->_width"); return $this->_width; } /** * * @return Number */ function get_height() { //Helpers::dompdf_debug("trace", ": returns $this->_height"); return $this->_height; } /** * Returns the current page number * * @return int */ function get_page_number() { return $this->_pdf->PageNo(); } /** * Sets the current page number * * @param int $num */ function set_page_number($num) { $this->_pdf->setPage($num); } /** * Returns the total number of pages * * @return int */ function get_page_count() { return $this->_pdf->getNumPages(); } /** * Sets the total number of pages * * Who uses it ??? * * @param int $count */ function set_page_count($count) { //$this->_page_count = (int)$count; //throw new DOMPDF_Exception("TCPDF does not support setting the page count."); $_dompdf_warnings[] = "TCPDF does not support setting the page count."; } /** * Draws a line from x1,y1 to x2,y2 * * See {@link Style::munge_color()} for the format of the color array. * See {@link Cpdf::setLineStyle()} for a description of the format of the * $style parameter (aka dash). * * @param float $x1 * @param float $y1 * @param float $x2 * @param float $y2 * @param array $color * @param float $width * @param array $style */ function line($x1, $y1, $x2, $y2, $color, $width, $style = array(), $blend = "Normal", $opacity = 1.0) { //Helpers::dompdf_debug("trace", "($x1, $y1, $x2, $y2, [$color[0], $color[1], $color[2]], $width, $style, $blend, $opacity)"); //$this->_set_stroke_color($color); //$this->_set_line_style($width, "butt", "", $style); $this->_set_line_transparency($blend, $opacity); $this->_pdf->Line($x1, $y1, $x2, $y2, $this->_make_line_style($color, $width, "butt", "", $style)); } function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) { //*** $this->_set_stroke_color($color); $this->_set_line_style($width, "butt", "", $style); $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false); } /** * Draws a rectangle at x1,y1 with width w and height h * * See {@link Style::munge_color()} for the format of the color array. * See {@link Cpdf::setLineStyle()} for a description of the $style * parameter (aka dash) * * @param float $x1 * @param float $y1 * @param float $w * @param float $h * @param array $color * @param float $width * @param array $style */ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null, $blend = "Normal", $opacity = 1.0) { //Helpers::dompdf_debug("trace", "($x1, $y1, $w, $h, [$color[0], $color[1], $color[2]], $width, $style, $blend, $opacity)"); //$this->_set_stroke_color($color); //$this->_set_line_style($width, "square", "miter", $style); $this->_set_line_transparency($blend, $opacity); $this->_pdf->Rect($x1, $y1, $w, $h, 'D', $this->_make_line_style($color, $width, "square", "miter", $style)); } /** * Draws a filled rectangle at x1,y1 with width w and height h * * See {@link Style::munge_color()} for the format of the color array. * * @param float $x1 * @param float $y1 * @param float $w * @param float $h * @param array $color */ function filled_rectangle($x1, $y1, $w, $h, $color, $blend = "Normal", $opacity = 1.0) { //var_dump($color); //Helpers::dompdf_debug("trace", "($x1, $y1, $w, $h, [$color[0], $color[1], $color[2]], $blend, $opacity)"); //$this->_set_stroke_color($color); //$this->_set_fill_color($color); //$this->_set_line_style(1, "square", "miter", array()); $this->_set_line_transparency($blend, $opacity); $this->_set_fill_transparency($blend, $opacity); $this->_pdf->Rect($x1, $y1, $w, $h, 'F', $this->_make_line_style($color, 1, "square", "miter", array()), $this->_get_rgb($color)); //*** } /** * Starts a clipping rectangle at x1,y1 with width w and height h * * @param float $x1 * @param float $y1 * @param float $w * @param float $h */ function clipping_rectangle($x1, $y1, $w, $h) {} function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { //*** $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL); } /** * Ends the last clipping shape */ function clipping_end() {} /** * Save current state */ function save() {} /** * Restore last state */ function restore() {} /** * Rotate */ function rotate($angle, $x, $y) {} /** * Skew */ function skew($angle_x, $angle_y, $x, $y) {} /** * Scale */ function scale($s_x, $s_y, $x, $y) {} /** * Translate */ function translate($t_x, $t_y) {} /** * Transform */ function transform($a, $b, $c, $d, $e, $f) {} /** * Draws a polygon * * The polygon is formed by joining all the points stored in the $points * array. $points has the following structure: * * array(0 => x1, * 1 => y1, * 2 => x2, * 3 => y2, * ... * ); * * * See {@link Style::munge_color()} for the format of the color array. * See {@link Cpdf::setLineStyle()} for a description of the $style * parameter (aka dash) * * @param array $points * @param array $color * @param float $width * @param array $style * @param bool $fill Fills the polygon if true */ function polygon($points, $color, $width = null, $style = null, $fill = false, $blend = "Normal", $opacity = 1.0) { //Helpers::dompdf_debug("trace", "($points, [$color[0], $color[1], $color[2]], $width, $style, $fill, $blend, $opacity)"); //$this->_set_fill_color($color); //$this->_set_stroke_color($color); $this->_set_line_transparency($blend, $opacity); if ($fill) { $this->_set_fill_transparency($blend, $opacity); } //if (!$fill && isset ($width)) { // $this->_set_line_style($width, "square", "miter", $style); //} $this->_pdf->Polygon($points, $fill ? 'F' : '', $this->_make_line_style($color, $width, "square", "miter", $style), $this->_get_rgb($color)); } /** * Draws a circle at $x,$y with radius $r * * See {@link Style::munge_color()} for the format of the color array. * See {@link Cpdf::setLineStyle()} for a description of the $style * parameter (aka dash) * * @param float $x * @param float $y * @param float $r * @param array $color * @param float $width * @param array $style * @param bool $fill Fills the circle if true */ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false, $blend = "Normal", $opacity = 1.0) { //Helpers::dompdf_debug("trace", "($x, $y, $r, [$color[0], $color[1], $color[2]], $width, $style, $fill, $blend, $opacity)"); //$this->_set_fill_color($color); //$this->_set_stroke_color($color); $this->_set_line_transparency($blend, $opacity); if ($fill) { $this->_set_fill_transparency($blend, $opacity); } //if (!$fill && isset ($width)) { // $this->_set_line_style($width, "round", "round", $style); //} $this->_pdf->Circle($x, $y, $r, 0, 360, $fill ? 'F' : '', $this->_make_line_style($color, $width, "round", "round", $style), $this->_get_rgb($color)); } /** * Add an image to the pdf. * * The image is placed at the specified x and y coordinates with the * given width and height. * * @param string $img_url the path to the image * @param string $img_type the type (e.g. extension) of the image * @param float $x x position * @param float $y y position * @param int $w width (in pixels) * @param int $h height (in pixels) */ function image($img, $x, $y, $w, $h, $resolution = "normal") { //*** if ($img_type == 'gif') { // $img_url = $this->_convert_gif_to_png($img_url); // $img_type = 'png'; // } $path_parts = pathinfo($img); $img_url = $img; $img_type = strtolower($path_parts['extension']); if ($img_type == 'gif') { $img_url = $this->_convert_gif_to_png($img_url); $img_type = 'png'; } $this->_pdf->Image($img_url, $x, $y, $w, $h, $img_type); } /** * Writes text at the specified x and y coordinates * * See {@link Style::munge_color()} for the format of the color array. * * @param float $x * @param float $y * @param string $text the text to write * @param string $font the font file to use * @param float $size the font size, in points * @param array $color * @param float $adjust word spacing adjustment */ //***function text($x, $y, $text, $font, $size, $color = array (0, 0, 0), $adjust = 0, $angle = 0, $blend = "Normal", $opacity = 1.0) { function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $adjust = 0.0, $char_space = 0.0, $angle = 0.0) { //*** //Helpers::dompdf_debug("trace", "($x, $y, $text, ". basename($font) .", $size, [$color[0], $color[1], $color[2]], $adjust, $angle, $blend, $opacity)"); list ($r, $g, $b) = $this->_get_rgb($color); $this->_pdf->SetTextColor($r, $g, $b); //***$this->_set_line_transparency($blend, $opacity); //***$this->_set_fill_transparency($blend, $opacity); $fontdata = $this->_get_font($font); $this->_pdf->SetFont($fontdata['family'], $fontdata['style'] /*strpos($font, '-Bold') === false ? '' : 'B'*/, $size, $font); //$this->_pdf->SetFontSize($size); // ??? if ($adjust > 0) { $a = explode(' ', $text); //$this->_pdf->SetXY($x - 3, $y + (self::FONT_HEIGHT_SCALE - 1) * $size); $this->_pdf->SetXY($x, $y); //$y += self::FONT_HEIGHT_SCALE * $size + 1; for ($i = 0; $i < count($a)-1; $i++) { $this->_pdf->Write($size, $a[$i].' ', ''); //$this->_pdf->Text($x, $y, $a[$i].' '); $this->_pdf->SetX($this->_pdf->GetX()+$adjust); //$x += $this->_pdf->GetX() + $adjust; } $this->_pdf->Write($size, $a[$i], ''); //$this->_pdf->Text($x, $y, $a[$i].' '); } else { if ($angle != 0) { $this->_pdf->StartTransform(); //$y += self::FONT_HEIGHT_SCALE * $size; //$y += $size; $this->_pdf->Rotate(-$angle, $x, $y); $this->_pdf->Text($x, $y, $text, false, false, true, 0, 0, '', 0, '', 0, false, 'T', 'T'); $this->_pdf->StopTransform(); } else { //$pippo = $this->_pdf->getFontAscent($fontdata['family'], $fontdata['style'], $size); //$y += $pippo / 8; //$y = $y - 0.85 * $size; // + 0.8 * $size; $this->_pdf->Text($x, $y, $text, false, false, true, 0, 0, '', 0, '', 0, false, 'T', 'T'); } } } /** * Add a named destination (similar to ... in html) * * @param string $anchorname The name of the named destination */ function add_named_dest($anchorname) { //Helpers::dompdf_debug("trace", "($anchorname)"); $link = $this->_pdf->AddLink(); $this->_pdf->SetLink($link, -1, -1); $this->_nameddest[$anchorname] = $link; } /** * Add a link to the pdf * * @param string $url The url to link to * @param float $x The x position of the link * @param float $y The y position of the link * @param float $width The width of the link * @param float $height The height of the link */ function add_link($url, $x, $y, $width, $height) { //Helpers::dompdf_debug("trace", "($url, $x, $y, $width, $height)"); if (strpos($url, '#') === 0) { // Local link $name = substr($url, 1); if ($name) { $page = $this->_pdf->PageNo(); //$this->_pdf->Link($x, $y, $width, $height, $this->_nameddest[$name]); $this->_internal_links[] = compact('page', 'name', 'x', 'y', 'width', 'height'); //array($this->_pdf->PageNo(), $name, $x, $y, $width, $height); } } else { $this->_pdf->Link($x, $y, $width, $height, rawurldecode($url)); } } /** * Calculates text size, in points * * @param string $text the text to be sized * @param string $font the desired font * @param float $size the desired font size * @param float $spacing word spacing, if any * @return float */ function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) { // Determine the additional width due to extra spacing $num_spaces = mb_substr_count($text, " "); $delta = $word_spacing*$num_spaces; $fontdata = $this->_get_font($font); $result = $this->_pdf->GetStringWidth($text, $fontdata['family'], $fontdata['style'], $size)+$delta; //$font); //Helpers::dompdf_debug("trace", "($text, ". basename($font) .", $size, $word_spacing, $char_spacing): returns $result"); return $result; } /** * Calculates font height, in points * * TCPDF lacks a method to get the font height * * @param string $font * @param float $size * @return float */ function get_font_height($font, $size) { if (!$font) { $fontFamily = $this->_pdf->getFontFamily(); $fontStyle = $this->_pdf->getFontStyle(); } else { $fontdata = $this->_get_font($font); $fontFamily = $fontdata['family']; $fontStyle = $fontdata['style']; $this->_pdf->SetFont($fontFamily, $fontStyle, $size, $font); } if (strpos($fontStyle, 'B') !== false) { $scale = self::FONT_HEIGHT_SCALE_BOLD; } else { $scale = self::FONT_HEIGHT_SCALE_NORMAL; } $result = $scale * $size; //Helpers::dompdf_debug("trace", "(". basename($font) .", $size): returns $result"); return $result; } function get_font_baseline($font, $size) { // b3 //***return $this->get_font_height($font, $size) / DOMPDF_FONT_HEIGHT_RATIO; $ratio = $this->_dompdf->get_option("font_height_ratio"); //*** return $this->get_font_height($font, $size) / $ratio; //*** } /** * Sets the opacity * * @param $opacity * @param $mode */ function set_opacity($opacity, $mode = "Normal") { //Helpers::dompdf_debug("trace", "($opacity, $mode)"); $this->_set_line_transparency($mode, $opacity); $this->_set_fill_transparency($mode, $opacity); } function set_default_view($view, $options = array()) { // b3 array_unshift($options, $view); $currentPage = $this->_pdf->currentPage; call_user_func_array(array($this->_pdf, "openHere"), $options); } /** * Starts a new page * * Subsequent drawing operations will appear on the new page. */ function new_page() { //Helpers::dompdf_debug("trace", "()"); // Add objects to the current page $this->_place_objects(); //$this->_pdf->endPage(); $this->_pdf->lastPage(); $this->_pdf->AddPage(); //$this->_page_count++; $this->_pages[] = $this->_pdf->PageNo(); return $this->_pdf->getNumPages(); } /** * Streams the PDF directly to the browser * * @param string $filename the name of the PDF file * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0 */ function stream($filename, $options = null) { //Helpers::dompdf_debug("trace", "($filename, $options)"); // Add page text $this->_add_page_text(); $this->_add_internal_links(); // TCPDF expects file name with extension (cf. Cpdf expects file name without extension) //*** if (!preg_match('/\.pdf$/', $filename)) $filename .= ".pdf"; $options["Content-Disposition"] = $filename; if ( isset($options["compress"]) && $options["compress"] == 0 ) $compress = false; else $compress = true; $this->_pdf->SetCompression($compress); $this->_pdf->Output($filename, $options["Attachment"] ? "D" : "I"); // *** } /** * Returns the PDF as a string * * @param array $options associative array: 'compress' => 1 or 0 * @return string */ function output($options = null) { //Helpers::dompdf_debug("trace", "($options[compress])"); // Add page text $this->_add_page_text(); $this->_add_internal_links(); $this->_place_objects(); if ( isset($options["compress"]) && $options["compress"] == 0 ) { $compress = false; } else { $compress = true; } $this->_pdf->SetCompression($compress); return $this->_pdf->Output('', 'S'); } /** * * @param $code * @return unknown_type */ function javascript($code) { // STUB } /** * Other public methods callable from user pages */ /** * Writes text at the specified x and y coordinates on every page * * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced * with their current values. * * See {@link Style::munge_colour()} for the format of the colour array. * * @param float $x * @param float $y * @param string $text the text to write * @param string $font the font file to use * @param float $size the font size, in points * @param array $color * @param float $adjust word spacing adjustment * @param float $angle angle to write the text at, measured CW starting from the x-axis */ function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $adjust = 0, $angle = 0) { //Helpers::dompdf_debug("trace", "($x, $y, $text, ". basename($font) .", $size, $color, $adjust, $angle, $blend, $opacity)"); $_t = "text"; $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "adjust", "angle"); } /** * Processes a script on every page * * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available. * * This function can be used to add page numbers to all pages * after the first one, for example. * * @param string $code the script code * @param string $type the language type for script */ function page_script($code, $type = "text/php") { $_t = "script"; $this->_page_text[] = compact("_t", "code", "type"); } } // Workaround for idiotic limitation on statics... \Dompdf\Adapter\TCPDF::$PAPER_SIZES = \Dompdf\Adapter\Cpdf::$PAPER_SIZES; /** * Class added to access the protected methods/variables of TCPDF * * It assumes that $this->page does not change while an object is opened */ class My_TCPDF extends \TCPDF { //*** private $dompdf_num_objects = 0; private $dompdf_objects = array(); private $dompdf_num_stack = 0; private $dompdf_stack = array(); function getPageBuffer($page) { return parent::getPageBuffer($page); } function setPageBuffer($page, $data, $append=false) { parent::setPageBuffer($page, $data, $append); } /** * save the TCPDF output buffer content in a stack and initialize it to an empty string * the function will return an object ID * * NOTE: can this method be called again without first issuing a closeObject()? */ function openObject() { //Helpers::dompdf_debug("trace", "Enter My_TCPDF.openObject(state = $this->state)"); if ($this->state == 2) { $curr_buffer = $this->getPageBuffer($this->page); } else { $curr_buffer = $this->getBuffer(); } $this->dompdf_num_stack++; $this->dompdf_stack[$this->dompdf_num_stack] = array('c' => $curr_buffer, 'p' => $this->page, 'g' => $this->getGraphicVars()); //Helpers::dompdf_debug("trace", "---------> " . $curr_buffer); if ($this->state == 2) { $this->setPageBuffer($this->page, ''); } else { $this->buffer = ''; $this->bufferlen = strlen($this->buffer); } $this->page = 1; // some output is not done if page = 0 (e.g SetDrawColor()) $this->dompdf_num_objects++; $this->dompdf_objects[$this->dompdf_num_objects] = array('c' => '', 'p' => 0, 'g' => null); return $this->dompdf_num_objects; } /** * restore the saved TCPDF output buffer content */ function closeObject() { //Helpers::dompdf_debug("trace", "Enter My_TCPDF.closeObject(state = $this->state)"); if ($this->dompdf_num_stack > 0) { if ($this->state == 2) { $curr_buffer = $this->getPageBuffer($this->page); } else { $curr_buffer = $this->getBuffer(); } $this->dompdf_objects[$this->dompdf_num_objects]['c'] = $curr_buffer; $this->dompdf_objects[$this->dompdf_num_objects]['p'] = $this->page; $this->dompdf_objects[$this->dompdf_num_objects]['g'] = $this->getGraphicVars(); //Helpers::dompdf_debug("trace", "--------- OBJECT buffer -------> " . $curr_buffer); $saved_stack = $this->dompdf_stack[$this->dompdf_num_stack]; //Helpers::dompdf_debug("trace", "--------- saved buffer -------> " . $saved_stack['c']); if ($this->state == 2) { $this->setPageBuffer($this->page, $saved_stack['c']); } else { $this->buffer = $saved_stack['c']; $this->bufferlen = strlen($this->buffer); } $this->page = $saved_stack['p']; $this->setGraphicVars($saved_stack['g']); unset($this->dompdf_stack[$this->dompdf_num_stack]); $this->dompdf_num_stack--; } } /** * reopen an existing object for editing * save the TCPDF output buffer content in the stack and initialize it with the contents of the object being reopened */ function reopenObject($id) { //Helpers::dompdf_debug("trace", "Enter My_TCPDF.reopenObject($id)"); if ($this->state == 2) { $curr_buffer = $this->getPageBuffer($this->page); } else { $curr_buffer = $this->getBuffer(); } $this->dompdf_num_stack++; $this->dompdf_stack[$this->dompdf_num_stack] = array('c' => $curr_buffer, 'p' => $this->page, 'g' => $this->getGraphicVars()); if ($this->state == 2) { $this->setPageBuffer($this->page, $this->dompdf_objects[$id]['c']); } else { $this->buffer = $this->dompdf_objects[$id]['c']; $this->bufferlen = strlen($this->buffer); } $this->page = 1; //$this->page = $this->dompdf_objects[$id]['p']; $this->setGraphicVars($this->dompdf_objects[$id]['g']); } function getObject($id) { //Helpers::dompdf_debug("trace", "Enter My_TCPDF.getObject($id)"); return $this->dompdf_objects[$id]; } } function endsWith($haystack,$needle,$case=true) { if($case){return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)),$needle)===0);} return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)),$needle)===0); }