DOMPDF : Missing Images and Unapplied Stylesheets
I’m currently using DOMPDF to convert an HTML page to PDF. The generated PDFs are fine, except that some styles doesn’t seem to get applied and some images ended up missing. After setting $_dompdf_show_warnings to true to debug the process, I found out that relative paths with leading slashes are interpreted as absolute file paths in the local file system. For example, if my document root is at /var/www/html/ and the relative path of the stylesheet (in the html markup) is /css , DOMPDF interprets it as /css instead of /var/www/html/css. The same happens with the case of images.
The application is created with the Yii Framework and uses pretty urls (the usual stuff you see), so it will be almost impossible to use paths relative to the current directory (../). I needed to them to point relative to the top level (/), keeping the leading slashes.
The solution: trim leading slashes accordingly. I had to be careful doing that though, since DOMPDF loads some default styles found inside its include folder and referenced using absolute file path (with leading slashes).
Here are the files with the necessary changes applied. It’s just a matter of ltrim()-ming the url.
For the stylesheets:
dompdf/include/dompdf.cls.php
==
/** * Builds the {@link Frame_Tree}, loads any CSS and applies the styles to * the {@link Frame_Tree} */ protected function _process_html() { // ... long code removed for brevity. DO NOT REMOVE THE CODE FROM THE ACTUAL FILE // load <link rel="STYLESHEET" ... /> tags $links = $this->_xml->getElementsByTagName("link"); foreach ($links as $link) { if ( mb_strtolower($link->getAttribute("rel")) === "stylesheet" || mb_strtolower($link->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,]/", $link->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 = $link->getAttribute("href"); // Fix for relative paths with leading slash - Rolan $url = ltrim($url, '/'); $url = build_url($this->_protocol, $this->_base_host, $this->_base_path, $url); $this->_css->load_css_file($url); } } // ... more long code removed for brevity. DO NOT REMOVE THE CODE FROM THE ACTUAL FILE }
==
For the images:
dompdf/include/image_frame_decorator.cls.php
==
/** * Class constructor * * @param Frame $frame the frame to decorate * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls) */ function __construct(Frame $frame, DOMPDF $dompdf) { global $_dompdf_warnings; parent::__construct($frame, $dompdf); $url = $frame->get_node()->getAttribute("src"); // Fix for relative paths with leading slash - Rolan $url = ltrim($url, '/'); //debugpng if (DEBUGPNG) print '[__construct '.$url.']'; list($this->_image_url, $this->_image_ext) = Image_Cache::resolve_url($url, $dompdf->get_protocol(), $dompdf->get_host(), $dompdf->get_base_path()); if ( strrpos( $this->_image_url, DOMPDF_LIB_DIR . "/res/broken_image.png", 0) !== false && $alt = $frame->get_node()->getAttribute("alt") ) { $style = $frame->get_style(); $style->width = (4/3)*Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing); $style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size); } }
==
Note that I’m using the latest release (DOMPDF 0.6 beta 2) at the time of this writing.