Example #1
0
 function render(Frame $frame)
 {
     $style = $frame->get_style();
     $node = $frame->get_node();
     list($x, $y, $w, $h) = $frame->get_border_box();
     $this->_set_opacity($frame->get_opacity($style->opacity));
     if ($node->nodeName === "body") {
         $h = $frame->get_containing_block("h") - $style->length_in_pt(array($style->margin_top, $style->border_top_width, $style->border_bottom_width, $style->margin_bottom), $style->width);
     }
     // Handle anchors & links
     if ($node->nodeName === "a" && ($href = $node->getAttribute("href"))) {
         $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href);
         $this->_canvas->add_link($href, $x, $y, $w, $h);
     }
     // Draw our background, border and content
     list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
     if ($tl + $tr + $br + $bl > 0) {
         $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
     }
     if (($bg = $style->background_color) !== "transparent") {
         $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
     }
     if (($url = $style->background_image) && $url !== "none") {
         $this->_background_image($url, $x, $y, $w, $h, $style);
     }
     if ($tl + $tr + $br + $bl > 0) {
         $this->_canvas->clipping_end();
     }
     $border_box = array($x, $y, $w, $h);
     $this->_render_border($frame, $border_box);
     $this->_render_outline($frame, $border_box);
     if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutBlocks()) {
         $this->_debug_layout($frame->get_border_box(), "red");
         if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) {
             $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
         }
     }
     if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines() && $frame->get_decorator()) {
         foreach ($frame->get_decorator()->get_line_boxes() as $line) {
             $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
         }
     }
 }
Example #2
0
 /**
  * Resolve and fetch an image for use.
  *
  * @param string $url       The url of the image
  * @param string $protocol  Default protocol if none specified in $url
  * @param string $host      Default host if none specified in $url
  * @param string $base_path Default path if none specified in $url
  * @param Dompdf $dompdf    The Dompdf instance
  *
  * @throws ImageException
  * @return array             An array with two elements: The local path to the image and the image extension
  */
 static function resolve_url($url, $protocol, $host, $base_path, Dompdf $dompdf)
 {
     self::$_dompdf = $dompdf;
     $protocol = mb_strtolower($protocol);
     $parsed_url = Helpers::explode_url($url);
     $message = null;
     $remote = $protocol && $protocol !== "file://" || $parsed_url['protocol'] != "";
     $data_uri = strpos($parsed_url['protocol'], "data:") === 0;
     $full_url = null;
     $enable_remote = $dompdf->get_option("enable_remote");
     try {
         // Remote not allowed and is not DataURI
         if (!$enable_remote && $remote && !$data_uri) {
             throw new ImageException("Remote file access is disabled.", E_WARNING);
         } else {
             if ($enable_remote && $remote || $data_uri) {
                 // Download remote files to a temporary directory
                 $full_url = Helpers::build_url($protocol, $host, $base_path, $url);
                 // From cache
                 if (isset(self::$_cache[$full_url])) {
                     $resolved_url = self::$_cache[$full_url];
                 } else {
                     $tmp_dir = $dompdf->get_option("temp_dir");
                     $resolved_url = tempnam($tmp_dir, "ca_dompdf_img_");
                     $image = "";
                     if ($data_uri) {
                         if ($parsed_data_uri = Helpers::parse_data_uri($url)) {
                             $image = $parsed_data_uri['data'];
                         }
                     } else {
                         set_error_handler(array("\\Dompdf\\Helpers", "record_warnings"));
                         $image = file_get_contents($full_url, null, $dompdf->getHttpContext());
                         restore_error_handler();
                     }
                     // Image not found or invalid
                     if (strlen($image) == 0) {
                         $msg = $data_uri ? "Data-URI could not be parsed" : "Image not found";
                         throw new ImageException($msg, E_WARNING);
                     } else {
                         //e.g. fetch.php?media=url.jpg&cache=1
                         //- Image file name might be one of the dynamic parts of the url, don't strip off!
                         //- a remote url does not need to have a file extension at all
                         //- local cached file does not have a matching file extension
                         //Therefore get image type from the content
                         file_put_contents($resolved_url, $image);
                     }
                 }
             } else {
                 $resolved_url = Helpers::build_url($protocol, $host, $base_path, $url);
             }
         }
         // Check if the local file is readable
         if (!is_readable($resolved_url) || !filesize($resolved_url)) {
             throw new ImageException("Image not readable or empty", E_WARNING);
         } else {
             list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $dompdf->getHttpContext());
             // Known image type
             if ($width && $height && in_array($type, array("gif", "png", "jpeg", "bmp", "svg"))) {
                 //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup.
                 //Only execute on successful caching of remote image.
                 if ($enable_remote && $remote || $data_uri) {
                     self::$_cache[$full_url] = $resolved_url;
                 }
             } else {
                 throw new ImageException("Image type unknown", E_WARNING);
             }
         }
     } catch (ImageException $e) {
         $resolved_url = self::$broken_image;
         $type = "png";
         $message = "Image not found or type unknown";
         Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n {$url}", $e->getFile(), $e->getLine());
     }
     return array($resolved_url, $type, $message);
 }
Example #3
0
 function render(Frame $frame)
 {
     $style = $frame->get_style();
     if (!$frame->get_first_child()) {
         return;
     }
     // No children, no service
     // Draw the left border if applicable
     $bp = $style->get_border_properties();
     $widths = array($style->length_in_pt($bp["top"]["width"]), $style->length_in_pt($bp["right"]["width"]), $style->length_in_pt($bp["bottom"]["width"]), $style->length_in_pt($bp["left"]["width"]));
     // Draw the background & border behind each child.  To do this we need
     // to figure out just how much space each child takes:
     list($x, $y) = $frame->get_first_child()->get_position();
     $w = null;
     $h = 0;
     //     $x += $widths[3];
     //     $y += $widths[0];
     $this->_set_opacity($frame->get_opacity($style->opacity));
     $first_row = true;
     foreach ($frame->get_children() as $child) {
         list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
         if (!is_null($w) && $child_x < $x + $w) {
             //This branch seems to be supposed to being called on the first part
             //of an inline html element, and the part after the if clause for the
             //parts after a line break.
             //But because $w initially mostly is 0, and gets updated only on the next
             //round, this seem to be never executed and the common close always.
             // The next child is on another line.  Draw the background &
             // borders on this line.
             // Background:
             if (($bg = $style->background_color) !== "transparent") {
                 $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
             }
             if (($url = $style->background_image) && $url !== "none") {
                 $this->_background_image($url, $x, $y, $w, $h, $style);
             }
             // If this is the first row, draw the left border
             if ($first_row) {
                 if ($bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0) {
                     $method = "_border_" . $bp["left"]["style"];
                     $this->{$method}($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
                 }
                 $first_row = false;
             }
             // Draw the top & bottom borders
             if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0) {
                 $method = "_border_" . $bp["top"]["style"];
                 $this->{$method}($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
             }
             if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0) {
                 $method = "_border_" . $bp["bottom"]["style"];
                 $this->{$method}($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
             }
             // Handle anchors & links
             $link_node = null;
             if ($frame->get_node()->nodeName === "a") {
                 $link_node = $frame->get_node();
             } else {
                 if ($frame->get_parent()->get_node()->nodeName === "a") {
                     $link_node = $frame->get_parent()->get_node();
                 }
             }
             if ($link_node && ($href = $link_node->getAttribute("href"))) {
                 $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href);
                 $this->_canvas->add_link($href, $x, $y, $w, $h);
             }
             $x = $child_x;
             $y = $child_y;
             $w = $child_w;
             $h = $child_h;
             continue;
         }
         if (is_null($w)) {
             $w = $child_w;
         } else {
             $w += $child_w;
         }
         $h = max($h, $child_h);
         if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutInline")) {
             $this->_debug_layout($child->get_border_box(), "blue");
             if ($this->_dompdf->get_option("debugLayoutPaddingBox")) {
                 $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5));
             }
         }
     }
     // Handle the last child
     if (($bg = $style->background_color) !== "transparent") {
         $this->_canvas->filled_rectangle($x + $widths[3], $y + $widths[0], $w, $h, $bg);
     }
     //On continuation lines (after line break) of inline elements, the style got copied.
     //But a non repeatable background image should not be repeated on the next line.
     //But removing the background image above has never an effect, and removing it below
     //removes it always, even on the initial line.
     //Need to handle it elsewhere, e.g. on certain ...clone()... usages.
     // Repeat not given: default is Style::__construct
     // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ...
     //different position? $this->_background_image($url, $x, $y, $w, $h, $style);
     if (($url = $style->background_image) && $url !== "none") {
         $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
     }
     // Add the border widths
     $w += $widths[1] + $widths[3];
     $h += $widths[0] + $widths[2];
     // make sure the border and background start inside the left margin
     $left_margin = $style->length_in_pt($style->margin_left);
     $x += $left_margin;
     // If this is the first row, draw the left border too
     if ($first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0) {
         $method = "_border_" . $bp["left"]["style"];
         $this->{$method}($x, $y, $h, $bp["left"]["color"], $widths, "left");
     }
     // Draw the top & bottom borders
     if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0) {
         $method = "_border_" . $bp["top"]["style"];
         $this->{$method}($x, $y, $w, $bp["top"]["color"], $widths, "top");
     }
     if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0) {
         $method = "_border_" . $bp["bottom"]["style"];
         $this->{$method}($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
     }
     //    Helpers::var_dump(get_class($frame->get_next_sibling()));
     //    $last_row = get_class($frame->get_next_sibling()) !== 'Inline';
     // Draw the right border if this is the last row
     if ($bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0) {
         $method = "_border_" . $bp["right"]["style"];
         $this->{$method}($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
     }
     // Only two levels of links frames
     $link_node = null;
     if ($frame->get_node()->nodeName === "a") {
         $link_node = $frame->get_node();
         if (($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id"))) {
             $this->_canvas->add_named_dest($name);
         }
     }
     if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") {
         $link_node = $frame->get_parent()->get_node();
     }
     // Handle anchors & links
     if ($link_node) {
         if ($href = $link_node->getAttribute("href")) {
             $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href);
             $this->_canvas->add_link($href, $x, $y, $w, $h);
         }
     }
 }
Example #4
0
 protected function _image($val)
 {
     $DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss();
     $parsed_url = "none";
     if (mb_strpos($val, "url") === false) {
         $path = "none";
         //Don't resolve no image -> otherwise would prefix path and no longer recognize as none
     } else {
         $val = preg_replace("/url\\(['\"]?([^'\")]+)['\"]?\\)/", "\\1", trim($val));
         // Resolve the url now in the context of the current stylesheet
         $parsed_url = Helpers::explode_url($val);
         if ($parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "") {
             if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\') {
                 $path = $_SERVER["DOCUMENT_ROOT"] . '/';
             } else {
                 $path = $this->_stylesheet->get_base_path();
             }
             $path .= $parsed_url["path"] . $parsed_url["file"];
             $path = realpath($path);
             // If realpath returns FALSE then specifically state that there is no background image
             if (!$path) {
                 $path = 'none';
             }
         } else {
             $path = Helpers::build_url($this->_stylesheet->get_protocol(), $this->_stylesheet->get_host(), $this->_stylesheet->get_base_path(), $val);
         }
     }
     if ($DEBUGCSS) {
         print "<pre>[_image\n";
         print_r($parsed_url);
         print $this->_stylesheet->get_protocol() . "\n" . $this->_stylesheet->get_base_path() . "\n" . $path . "\n";
         print "_image]</pre>";
     }
     return $path;
 }
Example #5
0
 /**
  * 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)
 {
     $y = $this->y($y) - $height;
     if (strpos($url, '#') === 0) {
         // Local link
         $name = substr($url, 1);
         if ($name) {
             $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} destname=" . substr($url, 1) . " linewidth=0");
         }
     } else {
         list($proto, $host, $path, $file) = Helpers::explode_url($url);
         if ($proto == "" || $proto === "file://") {
             return;
         }
         // Local links are not allowed
         $url = Helpers::build_url($proto, $host, $path, $file);
         $url = '{' . rawurldecode($url) . '}';
         $action = $this->_pdf->create_action("URI", "url=" . $url);
         $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate={$action}} linewidth=0");
     }
 }
Example #6
0
 /**
  * parse @font-face{} sections
  * http://www.w3.org/TR/css3-fonts/#the-font-face-rule
  *
  * @param string $str CSS @font-face rules
  * @return Style
  */
 private function _parse_font_face($str)
 {
     $descriptors = $this->_parse_properties($str);
     preg_match_all("/(url|local)\\s*\\([\"\\']?([^\"\\'\\)]+)[\"\\']?\\)\\s*(format\\s*\\([\"\\']?([^\"\\'\\)]+)[\"\\']?\\))?/i", $descriptors->src, $src);
     $sources = array();
     $valid_sources = array();
     foreach ($src[0] as $i => $value) {
         $source = array("local" => strtolower($src[1][$i]) === "local", "uri" => $src[2][$i], "format" => $src[4][$i], "path" => Helpers::build_url($this->_protocol, $this->_base_host, $this->_base_path, $src[2][$i]));
         if (!$source["local"] && in_array($source["format"], array("", "woff", "opentype", "truetype"))) {
             $valid_sources[] = $source;
         }
         $sources[] = $source;
     }
     // No valid sources
     if (empty($valid_sources)) {
         return;
     }
     $style = array("family" => $descriptors->get_font_family_raw(), "weight" => $descriptors->font_weight, "style" => $descriptors->font_style);
     $this->getFontMetrics()->registerFont($style, $valid_sources[0]["path"]);
 }
Example #7
0
 /**
  * Builds the {@link FrameTree}, loads any CSS and applies the styles to
  * the {@link FrameTree}
  */
 private function processHtml()
 {
     $this->tree->build_tree();
     $this->css->load_css_file(Stylesheet::getDefaultStylesheet(), Stylesheet::ORIG_UA);
     $acceptedmedia = Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES;
     $acceptedmedia[] = $this->options->getDefaultMediaType();
     // <base href="" />
     $base_nodes = $this->dom->getElementsByTagName("base");
     if ($base_nodes->length && ($href = $base_nodes->item(0)->getAttribute("href"))) {
         list($this->protocol, $this->baseHost, $this->basePath) = Helpers::explode_url($href);
     }
     // Set the base path of the Stylesheet to that of the file being processed
     $this->css->set_protocol($this->protocol);
     $this->css->set_host($this->baseHost);
     $this->css->set_base_path($this->basePath);
     // Get all the stylesheets so that they are processed in document order
     $xpath = new DOMXPath($this->dom);
     $stylesheets = $xpath->query("//*[name() = 'link' or name() = 'style']");
     foreach ($stylesheets as $tag) {
         switch (strtolower($tag->nodeName)) {
             // load <link rel="STYLESHEET" ... /> tags
             case "link":
                 if (mb_strtolower(stripos($tag->getAttribute("rel"), "stylesheet") !== false) || mb_strtolower($tag->getAttribute("type")) === "text/css") {
                     //Check if the css file is for an accepted media type
                     //media not given then always valid
                     $formedialist = preg_split("/[\\s\n,]/", $tag->getAttribute("media"), -1, PREG_SPLIT_NO_EMPTY);
                     if (count($formedialist) > 0) {
                         $accept = false;
                         foreach ($formedialist as $type) {
                             if (in_array(mb_strtolower(trim($type)), $acceptedmedia)) {
                                 $accept = true;
                                 break;
                             }
                         }
                         if (!$accept) {
                             //found at least one mediatype, but none of the accepted ones
                             //Skip this css file.
                             continue;
                         }
                     }
                     $url = $tag->getAttribute("href");
                     $url = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $url);
                     $this->css->load_css_file($url, Stylesheet::ORIG_AUTHOR);
                 }
                 break;
                 // load <style> tags
             // load <style> tags
             case "style":
                 // Accept all <style> tags by default (note this is contrary to W3C
                 // HTML 4.0 spec:
                 // http://www.w3.org/TR/REC-html40/present/styles.html#adef-media
                 // which states that the default media type is 'screen'
                 if ($tag->hasAttributes() && ($media = $tag->getAttribute("media")) && !in_array($media, $acceptedmedia)) {
                     continue;
                 }
                 $css = "";
                 if ($tag->hasChildNodes()) {
                     $child = $tag->firstChild;
                     while ($child) {
                         $css .= $child->nodeValue;
                         // Handle <style><!-- blah --></style>
                         $child = $child->nextSibling;
                     }
                 } else {
                     $css = $tag->nodeValue;
                 }
                 $this->css->load_css($css);
                 break;
         }
     }
 }