vendor/dompdf/dompdf/src/Dompdf.php line 264

Open in your IDE?
  1. <?php
  2. /**
  3.  * @package dompdf
  4.  * @link    http://dompdf.github.com/
  5.  * @author  Benj Carson <benjcarson@digitaljunkies.ca>
  6.  * @author  Fabien Ménager <fabien.menager@gmail.com>
  7.  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  8.  */
  9. namespace Dompdf;
  10. use DOMDocument;
  11. use DOMNode;
  12. use Dompdf\Adapter\CPDF;
  13. use DOMXPath;
  14. use Dompdf\Frame\Factory;
  15. use Dompdf\Frame\FrameTree;
  16. use HTML5_Tokenizer;
  17. use HTML5_TreeBuilder;
  18. use Dompdf\Image\Cache;
  19. use Dompdf\Css\Stylesheet;
  20. use Dompdf\Helpers;
  21. /**
  22.  * Dompdf - PHP5 HTML to PDF renderer
  23.  *
  24.  * Dompdf loads HTML and does its best to render it as a PDF.  It gets its
  25.  * name from the new DomDocument PHP5 extension.  Source HTML is first
  26.  * parsed by a DomDocument object.  Dompdf takes the resulting DOM tree and
  27.  * attaches a {@link Frame} object to each node.  {@link Frame} objects store
  28.  * positioning and layout information and each has a reference to a {@link
  29.  * Style} object.
  30.  *
  31.  * Style information is loaded and parsed (see {@link Stylesheet}) and is
  32.  * applied to the frames in the tree by using XPath.  CSS selectors are
  33.  * converted into XPath queries, and the computed {@link Style} objects are
  34.  * applied to the {@link Frame}s.
  35.  *
  36.  * {@link Frame}s are then decorated (in the design pattern sense of the
  37.  * word) based on their CSS display property ({@link
  38.  * http://www.w3.org/TR/CSS21/visuren.html#propdef-display}).
  39.  * Frame_Decorators augment the basic {@link Frame} class by adding
  40.  * additional properties and methods specific to the particular type of
  41.  * {@link Frame}.  For example, in the CSS layout model, block frames
  42.  * (display: block;) contain line boxes that are usually filled with text or
  43.  * other inline frames.  The Block therefore adds a $lines
  44.  * property as well as methods to add {@link Frame}s to lines and to add
  45.  * additional lines.  {@link Frame}s also are attached to specific
  46.  * AbstractPositioner and {@link AbstractFrameReflower} objects that contain the
  47.  * positioining and layout algorithm for a specific type of frame,
  48.  * respectively.  This is an application of the Strategy pattern.
  49.  *
  50.  * Layout, or reflow, proceeds recursively (post-order) starting at the root
  51.  * of the document.  Space constraints (containing block width & height) are
  52.  * pushed down, and resolved positions and sizes bubble up.  Thus, every
  53.  * {@link Frame} in the document tree is traversed once (except for tables
  54.  * which use a two-pass layout algorithm).  If you are interested in the
  55.  * details, see the reflow() method of the Reflower classes.
  56.  *
  57.  * Rendering is relatively straightforward once layout is complete. {@link
  58.  * Frame}s are rendered using an adapted {@link Cpdf} class, originally
  59.  * written by Wayne Munro, http://www.ros.co.nz/pdf/.  (Some performance
  60.  * related changes have been made to the original {@link Cpdf} class, and
  61.  * the {@link Dompdf\Adapter\CPDF} class provides a simple, stateless interface to
  62.  * PDF generation.)  PDFLib support has now also been added, via the {@link
  63.  * Dompdf\Adapter\PDFLib}.
  64.  *
  65.  *
  66.  * @package dompdf
  67.  */
  68. class Dompdf
  69. {
  70.     /**
  71.      * Version string for dompdf
  72.      *
  73.      * @var string
  74.      */
  75.     private $version 'dompdf';
  76.     /**
  77.      * DomDocument representing the HTML document
  78.      *
  79.      * @var DOMDocument
  80.      */
  81.     private $dom;
  82.     /**
  83.      * FrameTree derived from the DOM tree
  84.      *
  85.      * @var FrameTree
  86.      */
  87.     private $tree;
  88.     /**
  89.      * Stylesheet for the document
  90.      *
  91.      * @var Stylesheet
  92.      */
  93.     private $css;
  94.     /**
  95.      * Actual PDF renderer
  96.      *
  97.      * @var Canvas
  98.      */
  99.     private $canvas;
  100.     /**
  101.      * Desired paper size ('letter', 'legal', 'A4', etc.)
  102.      *
  103.      * @var string|array
  104.      */
  105.     private $paperSize;
  106.     /**
  107.      * Paper orientation ('portrait' or 'landscape')
  108.      *
  109.      * @var string
  110.      */
  111.     private $paperOrientation "portrait";
  112.     /**
  113.      * Callbacks on new page and new element
  114.      *
  115.      * @var array
  116.      */
  117.     private $callbacks = [];
  118.     /**
  119.      * Experimental caching capability
  120.      *
  121.      * @var string
  122.      */
  123.     private $cacheId;
  124.     /**
  125.      * Base hostname
  126.      *
  127.      * Used for relative paths/urls
  128.      * @var string
  129.      */
  130.     private $baseHost "";
  131.     /**
  132.      * Absolute base path
  133.      *
  134.      * Used for relative paths/urls
  135.      * @var string
  136.      */
  137.     private $basePath "";
  138.     /**
  139.      * Protocol used to request file (file://, http://, etc)
  140.      *
  141.      * @var string
  142.      */
  143.     private $protocol "";
  144.     /**
  145.      * The system's locale
  146.      *
  147.      * @var string
  148.      */
  149.     private $systemLocale null;
  150.     /**
  151.      * The system's mbstring internal encoding
  152.      *
  153.      * @var string
  154.      */
  155.     private $mbstringEncoding null;
  156.     /**
  157.      * The system's PCRE JIT configuration
  158.      *
  159.      * @var string
  160.      */
  161.     private $pcreJit null;
  162.     /**
  163.      * The default view of the PDF in the viewer
  164.      *
  165.      * @var string
  166.      */
  167.     private $defaultView "Fit";
  168.     /**
  169.      * The default view options of the PDF in the viewer
  170.      *
  171.      * @var array
  172.      */
  173.     private $defaultViewOptions = [];
  174.     /**
  175.      * Tells whether the DOM document is in quirksmode (experimental)
  176.      *
  177.      * @var bool
  178.      */
  179.     private $quirksmode false;
  180.     /**
  181.     * Protocol whitelist
  182.     *
  183.     * Protocols and PHP wrappers allowed in URLs. Full support is not
  184.     * guaranteed for the protocols/wrappers contained in this array.
  185.     *
  186.     * @var array
  187.     */
  188.     private $allowedProtocols = ["""file://""http://""https://"];
  189.     /**
  190.     * Local file extension whitelist
  191.     *
  192.     * File extensions supported by dompdf for local files.
  193.     *
  194.     * @var array
  195.     */
  196.     private $allowedLocalFileExtensions = ["htm""html"];
  197.     /**
  198.      * @var array
  199.      */
  200.     private $messages = [];
  201.     /**
  202.      * @var Options
  203.      */
  204.     private $options;
  205.     /**
  206.      * @var FontMetrics
  207.      */
  208.     private $fontMetrics;
  209.     /**
  210.      * The list of built-in fonts
  211.      *
  212.      * @var array
  213.      * @deprecated
  214.      */
  215.     public static $native_fonts = [
  216.         "courier""courier-bold""courier-oblique""courier-boldoblique",
  217.         "helvetica""helvetica-bold""helvetica-oblique""helvetica-boldoblique",
  218.         "times-roman""times-bold""times-italic""times-bolditalic",
  219.         "symbol""zapfdinbats"
  220.     ];
  221.     /**
  222.      * The list of built-in fonts
  223.      *
  224.      * @var array
  225.      */
  226.     public static $nativeFonts = [
  227.         "courier""courier-bold""courier-oblique""courier-boldoblique",
  228.         "helvetica""helvetica-bold""helvetica-oblique""helvetica-boldoblique",
  229.         "times-roman""times-bold""times-italic""times-bolditalic",
  230.         "symbol""zapfdinbats"
  231.     ];
  232.     /**
  233.      * Class constructor
  234.      *
  235.      * @param array|Options $options
  236.      */
  237.     public function __construct($options null)
  238.     {
  239.         if (isset($options) && $options instanceof Options) {
  240.             $this->setOptions($options);
  241.         } elseif (is_array($options)) {
  242.             $this->setOptions(new Options($options));
  243.         } else {
  244.             $this->setOptions(new Options());
  245.         }
  246.         $versionFile realpath(__DIR__ '/../VERSION');
  247.         if (file_exists($versionFile) && ($version trim(file_get_contents($versionFile))) !== false && $version !== '$Format:<%h>$') {
  248.             $this->version sprintf('dompdf %s'$version);
  249.         }
  250.         $this->setPhpConfig();
  251.         $this->paperSize $this->options->getDefaultPaperSize();
  252.         $this->paperOrientation $this->options->getDefaultPaperOrientation();
  253.         $this->setCanvas(CanvasFactory::get_instance($this$this->paperSize$this->paperOrientation));
  254.         $this->setFontMetrics(new FontMetrics($this->getCanvas(), $this->getOptions()));
  255.         $this->css = new Stylesheet($this);
  256.         $this->restorePhpConfig();
  257.     }
  258.     /**
  259.      * Save the system's existing locale, PCRE JIT, and MBString encoding
  260.      * configuration and configure the system for Dompdf processing
  261.      */
  262.     private function setPhpConfig()
  263.     {
  264.         if (sprintf('%.1f'1.0) !== '1.0') {
  265.             $this->systemLocale setlocale(LC_NUMERIC"0");
  266.             setlocale(LC_NUMERIC"C");
  267.         }
  268.         $this->pcreJit = @ini_get('pcre.jit');
  269.         @ini_set('pcre.jit''0');
  270.         $this->mbstringEncoding mb_internal_encoding();
  271.         mb_internal_encoding('UTF-8');
  272.     }
  273.     /**
  274.      * Restore the system's locale configuration
  275.      */
  276.     private function restorePhpConfig()
  277.     {
  278.         if ($this->systemLocale !== null) {
  279.             setlocale(LC_NUMERIC$this->systemLocale);
  280.             $this->systemLocale null;
  281.         }
  282.         if ($this->pcreJit !== null) {
  283.             @ini_set('pcre.jit'$this->pcreJit);
  284.             $this->pcreJit null;
  285.         }
  286.         if ($this->mbstringEncoding !== null) {
  287.             mb_internal_encoding($this->mbstringEncoding);
  288.             $this->mbstringEncoding null;
  289.         }
  290.     }
  291.     /**
  292.      * @param $file
  293.      * @deprecated
  294.      */
  295.     public function load_html_file($file)
  296.     {
  297.         $this->loadHtmlFile($file);
  298.     }
  299.     /**
  300.      * Loads an HTML file
  301.      * Parse errors are stored in the global array _dompdf_warnings.
  302.      *
  303.      * @param string $file a filename or url to load
  304.      * @param string $encoding Encoding of $file
  305.      *
  306.      * @throws Exception
  307.      */
  308.     public function loadHtmlFile($file$encoding null)
  309.     {
  310.         $this->setPhpConfig();
  311.         if (!$this->protocol && !$this->baseHost && !$this->basePath) {
  312.             [$this->protocol$this->baseHost$this->basePath] = Helpers::explode_url($file);
  313.         }
  314.         $protocol strtolower($this->protocol);
  315.         
  316.         $uri Helpers::build_url($this->protocol$this->baseHost$this->basePath$file);
  317.         if (!in_array($protocol$this->allowedProtocolstrue)) {
  318.             throw new Exception("Permission denied on $file. The communication protocol is not supported.");
  319.         }
  320.         if (!$this->options->isRemoteEnabled() && ($protocol !== "" && $protocol !== "file://")) {
  321.             throw new Exception("Remote file requested, but remote file download is disabled.");
  322.         }
  323.         if ($protocol === "" || $protocol === "file://") {
  324.             $realfile realpath($uri);
  325.             $chroot $this->options->getChroot();
  326.             $chrootValid false;
  327.             foreach ($chroot as $chrootPath) {
  328.                 $chrootPath realpath($chrootPath);
  329.                 if ($chrootPath !== false && strpos($realfile$chrootPath) === 0) {
  330.                     $chrootValid true;
  331.                     break;
  332.                 }
  333.             }
  334.             if ($chrootValid !== true) {
  335.                 throw new Exception("Permission denied on $file. The file could not be found under the paths specified by Options::chroot.");
  336.             }
  337.             $ext strtolower(pathinfo($realfilePATHINFO_EXTENSION));
  338.             if (!in_array($ext$this->allowedLocalFileExtensions)) {
  339.                 throw new Exception("Permission denied on $file. This file extension is forbidden");
  340.             }
  341.             if (!$realfile) {
  342.                 throw new Exception("File '$file' not found.");
  343.             }
  344.             $uri $realfile;
  345.         }
  346.         [$contents$http_response_header] = Helpers::getFileContent($uri$this->options->getHttpContext());
  347.         if ($contents === null) {
  348.             throw new Exception("File '$file' not found.");
  349.         }
  350.         // See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/
  351.         if (isset($http_response_header)) {
  352.             foreach ($http_response_header as $_header) {
  353.                 if (preg_match("@Content-Type:\s*[\w/]+;\s*?charset=([^\s]+)@i"$_header$matches)) {
  354.                     $encoding strtoupper($matches[1]);
  355.                     break;
  356.                 }
  357.             }
  358.         }
  359.         $this->restorePhpConfig();
  360.         $this->loadHtml($contents$encoding);
  361.     }
  362.     /**
  363.      * @param string $str
  364.      * @param string $encoding
  365.      * @deprecated
  366.      */
  367.     public function load_html($str$encoding null)
  368.     {
  369.         $this->loadHtml($str$encoding);
  370.     }
  371.     public function loadDOM($doc$quirksmode false) {
  372.         // Remove #text children nodes in nodes that shouldn't have
  373.         $tag_names = ["html""head""table""tbody""thead""tfoot""tr"];
  374.         foreach ($tag_names as $tag_name) {
  375.             $nodes $doc->getElementsByTagName($tag_name);
  376.             foreach ($nodes as $node) {
  377.                 self::removeTextNodes($node);
  378.             }
  379.         }
  380.         $this->dom $doc;
  381.         $this->quirksmode $quirksmode;
  382.         $this->tree = new FrameTree($this->dom);
  383.     }
  384.     /**
  385.      * Loads an HTML string
  386.      * Parse errors are stored in the global array _dompdf_warnings.
  387.      *
  388.      * @param string $str HTML text to load
  389.      * @param string $encoding Encoding of $str
  390.      */
  391.     public function loadHtml($str$encoding null)
  392.     {
  393.         $this->setPhpConfig();
  394.         // Determine character encoding when $encoding parameter not used
  395.         if ($encoding === null) {
  396.             mb_detect_order('auto');
  397.             if (($encoding mb_detect_encoding($strnulltrue)) === false) {
  398.                 //"auto" is expanded to "ASCII,JIS,UTF-8,EUC-JP,SJIS"
  399.                 $encoding "auto";
  400.             }
  401.         }
  402.         if (in_array(strtoupper($encoding), array('UTF-8','UTF8')) === false) {
  403.             $str mb_convert_encoding($str'UTF-8'$encoding);
  404.             //Update encoding after converting
  405.             $encoding 'UTF-8';
  406.         }
  407.         $metatags = [
  408.             '@<meta\s+http-equiv="Content-Type"\s+content="(?:[\w/]+)(?:;\s*?charset=([^\s"]+))?@i',
  409.             '@<meta\s+content="(?:[\w/]+)(?:;\s*?charset=([^\s"]+))"?\s+http-equiv="Content-Type"@i',
  410.             '@<meta [^>]*charset\s*=\s*["\']?\s*([^"\' ]+)@i',
  411.         ];
  412.         foreach ($metatags as $metatag) {
  413.             if (preg_match($metatag$str$matches)) {
  414.                 if (isset($matches[1]) && in_array($matches[1], mb_list_encodings())) {
  415.                     $document_encoding $matches[1];
  416.                     break;
  417.                 }
  418.             }
  419.         }
  420.         if (isset($document_encoding) && in_array(strtoupper($document_encoding), ['UTF-8','UTF8']) === false) {
  421.             $str preg_replace('/charset=([^\s"]+)/i''charset=UTF-8'$str);
  422.         } elseif (isset($document_encoding) === false && strpos($str'<head>') !== false) {
  423.             $str str_replace('<head>''<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">'$str);
  424.         } elseif (isset($document_encoding) === false) {
  425.             $str '<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">' $str;
  426.         }
  427.         // remove BOM mark from UTF-8, it's treated as document text by DOMDocument
  428.         // FIXME: roll this into the encoding detection using UTF-8/16/32 BOM (http://us2.php.net/manual/en/function.mb-detect-encoding.php#91051)?
  429.         if (substr($str03) == chr(0xEF) . chr(0xBB) . chr(0xBF)) {
  430.             $str substr($str3);
  431.         }
  432.         // Store parsing warnings as messages
  433.         set_error_handler([Helpers::class, 'record_warnings']);
  434.         try {
  435.             // @todo Take the quirksmode into account
  436.             // http://hsivonen.iki.fi/doctype/
  437.             // https://developer.mozilla.org/en/mozilla's_quirks_mode
  438.             $quirksmode false;
  439.             if ($this->options->isHtml5ParserEnabled() && class_exists(HTML5_Tokenizer::class)) {
  440.                 $tokenizer = new HTML5_Tokenizer($str);
  441.                 $tokenizer->parse();
  442.                 $doc $tokenizer->save();
  443.                 $quirksmode = ($tokenizer->getTree()->getQuirksMode() > HTML5_TreeBuilder::NO_QUIRKS);
  444.             } else {
  445.                 // loadHTML assumes ISO-8859-1 unless otherwise specified on the HTML document header.
  446.                 // http://devzone.zend.com/1538/php-dom-xml-extension-encoding-processing/ (see #4)
  447.                 // http://stackoverflow.com/a/11310258/264628
  448.                 $doc = new DOMDocument("1.0"$encoding);
  449.                 $doc->preserveWhiteSpace true;
  450.                 $doc->loadHTML($str);
  451.                 $doc->encoding $encoding;
  452.                 // If some text is before the doctype, we are in quirksmode
  453.                 if (preg_match("/^(.+)<!doctype/i"ltrim($str), $matches)) {
  454.                     $quirksmode true;
  455.                 } // If no doctype is provided, we are in quirksmode
  456.                 elseif (!preg_match("/^<!doctype/i"ltrim($str), $matches)) {
  457.                     $quirksmode true;
  458.                 } else {
  459.                     // HTML5 <!DOCTYPE html>
  460.                     if (!$doc->doctype->publicId && !$doc->doctype->systemId) {
  461.                         $quirksmode false;
  462.                     }
  463.                     // not XHTML
  464.                     if (!preg_match("/xhtml/i"$doc->doctype->publicId)) {
  465.                         $quirksmode true;
  466.                     }
  467.                 }
  468.             }
  469.             $this->loadDOM($doc$quirksmode);
  470.         } finally {
  471.             restore_error_handler();
  472.             $this->restorePhpConfig();
  473.         }
  474.     }
  475.     /**
  476.      * @param DOMNode $node
  477.      * @deprecated
  478.      */
  479.     public static function remove_text_nodes(DOMNode $node)
  480.     {
  481.         self::removeTextNodes($node);
  482.     }
  483.     /**
  484.      * @param DOMNode $node
  485.      */
  486.     public static function removeTextNodes(DOMNode $node)
  487.     {
  488.         $children = [];
  489.         for ($i 0$i $node->childNodes->length$i++) {
  490.             $child $node->childNodes->item($i);
  491.             if ($child->nodeName === "#text") {
  492.                 $children[] = $child;
  493.             }
  494.         }
  495.         foreach ($children as $child) {
  496.             $node->removeChild($child);
  497.         }
  498.     }
  499.     /**
  500.      * Builds the {@link FrameTree}, loads any CSS and applies the styles to
  501.      * the {@link FrameTree}
  502.      */
  503.     private function processHtml()
  504.     {
  505.         $this->tree->build_tree();
  506.         $this->css->load_css_file($this->css->getDefaultStylesheet(), Stylesheet::ORIG_UA);
  507.         $acceptedmedia Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES;
  508.         $acceptedmedia[] = $this->options->getDefaultMediaType();
  509.         // <base href="" />
  510.         $base_nodes $this->dom->getElementsByTagName("base");
  511.         if ($base_nodes->length && ($href $base_nodes->item(0)->getAttribute("href"))) {
  512.             [$this->protocol$this->baseHost$this->basePath] = Helpers::explode_url($href);
  513.         }
  514.         // Set the base path of the Stylesheet to that of the file being processed
  515.         $this->css->set_protocol($this->protocol);
  516.         $this->css->set_host($this->baseHost);
  517.         $this->css->set_base_path($this->basePath);
  518.         // Get all the stylesheets so that they are processed in document order
  519.         $xpath = new DOMXPath($this->dom);
  520.         $stylesheets $xpath->query("//*[name() = 'link' or name() = 'style']");
  521.         /** @var \DOMElement $tag */
  522.         foreach ($stylesheets as $tag) {
  523.             switch (strtolower($tag->nodeName)) {
  524.                 // load <link rel="STYLESHEET" ... /> tags
  525.                 case "link":
  526.                     if (mb_strtolower(stripos($tag->getAttribute("rel"), "stylesheet") !== false) || // may be "appendix stylesheet"
  527.                         mb_strtolower($tag->getAttribute("type")) === "text/css"
  528.                     ) {
  529.                         //Check if the css file is for an accepted media type
  530.                         //media not given then always valid
  531.                         $formedialist preg_split("/[\s\n,]/"$tag->getAttribute("media"), -1PREG_SPLIT_NO_EMPTY);
  532.                         if (count($formedialist) > 0) {
  533.                             $accept false;
  534.                             foreach ($formedialist as $type) {
  535.                                 if (in_array(mb_strtolower(trim($type)), $acceptedmedia)) {
  536.                                     $accept true;
  537.                                     break;
  538.                                 }
  539.                             }
  540.                             if (!$accept) {
  541.                                 //found at least one mediatype, but none of the accepted ones
  542.                                 //Skip this css file.
  543.                                 break;
  544.                             }
  545.                         }
  546.                         $url $tag->getAttribute("href");
  547.                         $url Helpers::build_url($this->protocol$this->baseHost$this->basePath$url);
  548.                         $this->css->load_css_file($urlStylesheet::ORIG_AUTHOR);
  549.                     }
  550.                     break;
  551.                 // load <style> tags
  552.                 case "style":
  553.                     // Accept all <style> tags by default (note this is contrary to W3C
  554.                     // HTML 4.0 spec:
  555.                     // http://www.w3.org/TR/REC-html40/present/styles.html#adef-media
  556.                     // which states that the default media type is 'screen'
  557.                     if ($tag->hasAttributes() &&
  558.                         ($media $tag->getAttribute("media")) &&
  559.                         !in_array($media$acceptedmedia)
  560.                     ) {
  561.                         break;
  562.                     }
  563.                     $css "";
  564.                     if ($tag->hasChildNodes()) {
  565.                         $child $tag->firstChild;
  566.                         while ($child) {
  567.                             $css .= $child->nodeValue// Handle <style><!-- blah --></style>
  568.                             $child $child->nextSibling;
  569.                         }
  570.                     } else {
  571.                         $css $tag->nodeValue;
  572.                     }
  573.                     // Set the base path of the Stylesheet to that of the file being processed
  574.                     $this->css->set_protocol($this->protocol);
  575.                     $this->css->set_host($this->baseHost);
  576.                     $this->css->set_base_path($this->basePath);
  577.                     $this->css->load_css($cssStylesheet::ORIG_AUTHOR);
  578.                     break;
  579.             }
  580.             // Set the base path of the Stylesheet to that of the file being processed
  581.             $this->css->set_protocol($this->protocol);
  582.             $this->css->set_host($this->baseHost);
  583.             $this->css->set_base_path($this->basePath);
  584.         }
  585.     }
  586.     /**
  587.      * @param string $cacheId
  588.      * @deprecated
  589.      */
  590.     public function enable_caching($cacheId)
  591.     {
  592.         $this->enableCaching($cacheId);
  593.     }
  594.     /**
  595.      * Enable experimental caching capability
  596.      *
  597.      * @param string $cacheId
  598.      */
  599.     public function enableCaching($cacheId)
  600.     {
  601.         $this->cacheId $cacheId;
  602.     }
  603.     /**
  604.      * @param string $value
  605.      * @return bool
  606.      * @deprecated
  607.      */
  608.     public function parse_default_view($value)
  609.     {
  610.         return $this->parseDefaultView($value);
  611.     }
  612.     /**
  613.      * @param string $value
  614.      * @return bool
  615.      */
  616.     public function parseDefaultView($value)
  617.     {
  618.         $valid = ["XYZ""Fit""FitH""FitV""FitR""FitB""FitBH""FitBV"];
  619.         $options preg_split("/\s*,\s*/"trim($value));
  620.         $defaultView array_shift($options);
  621.         if (!in_array($defaultView$valid)) {
  622.             return false;
  623.         }
  624.         $this->setDefaultView($defaultView$options);
  625.         return true;
  626.     }
  627.     /**
  628.      * Renders the HTML to PDF
  629.      */
  630.     public function render()
  631.     {
  632.         $this->setPhpConfig();
  633.         $options $this->options;
  634.         $logOutputFile $options->getLogOutputFile();
  635.         if ($logOutputFile) {
  636.             if (!file_exists($logOutputFile) && is_writable(dirname($logOutputFile))) {
  637.                 touch($logOutputFile);
  638.             }
  639.             $startTime microtime(true);
  640.             if (is_writable($logOutputFile)) {
  641.                 ob_start();
  642.             }
  643.         }
  644.         $this->processHtml();
  645.         $this->css->apply_styles($this->tree);
  646.         // @page style rules : size, margins
  647.         $pageStyles $this->css->get_page_styles();
  648.         $basePageStyle $pageStyles["base"];
  649.         unset($pageStyles["base"]);
  650.         foreach ($pageStyles as $pageStyle) {
  651.             $pageStyle->inherit($basePageStyle);
  652.         }
  653.         $defaultOptionPaperSize $this->getPaperSize($options->getDefaultPaperSize());
  654.         // If there is a CSS defined paper size compare to the paper size used to create the canvas to determine a
  655.         // recreation need
  656.         if (is_array($basePageStyle->size)) {
  657.             $basePageStyleSize $basePageStyle->size;
  658.             $this->setPaper([00$basePageStyleSize[0], $basePageStyleSize[1]]);
  659.         }
  660.         $paperSize $this->getPaperSize();
  661.         if (
  662.             $defaultOptionPaperSize[2] !== $paperSize[2] ||
  663.             $defaultOptionPaperSize[3] !== $paperSize[3] ||
  664.             $options->getDefaultPaperOrientation() !== $this->paperOrientation
  665.         ) {
  666.             $this->setCanvas(CanvasFactory::get_instance($this$this->paperSize$this->paperOrientation));
  667.             $this->fontMetrics->setCanvas($this->getCanvas());
  668.         }
  669.         $canvas $this->getCanvas();
  670.         $root null;
  671.         foreach ($this->tree->get_frames() as $frame) {
  672.             // Set up the root frame
  673.             if (is_null($root)) {
  674.                 $root Factory::decorate_root($this->tree->get_root(), $this);
  675.                 continue;
  676.             }
  677.             // Create the appropriate decorators, reflowers & positioners.
  678.             Factory::decorate_frame($frame$this$root);
  679.         }
  680.         // Add meta information
  681.         $title $this->dom->getElementsByTagName("title");
  682.         if ($title->length) {
  683.             $canvas->add_info("Title"trim($title->item(0)->nodeValue));
  684.         }
  685.         $metas $this->dom->getElementsByTagName("meta");
  686.         $labels = [
  687.             "author" => "Author",
  688.             "keywords" => "Keywords",
  689.             "description" => "Subject",
  690.         ];
  691.         /** @var \DOMElement $meta */
  692.         foreach ($metas as $meta) {
  693.             $name mb_strtolower($meta->getAttribute("name"));
  694.             $value trim($meta->getAttribute("content"));
  695.             if (isset($labels[$name])) {
  696.                 $canvas->add_info($labels[$name], $value);
  697.                 continue;
  698.             }
  699.             if ($name === "dompdf.view" && $this->parseDefaultView($value)) {
  700.                 $canvas->set_default_view($this->defaultView$this->defaultViewOptions);
  701.             }
  702.         }
  703.         $root->set_containing_block(00$canvas->get_width(), $canvas->get_height());
  704.         $root->set_renderer(new Renderer($this));
  705.         // This is where the magic happens:
  706.         $root->reflow();
  707.         // Clean up cached images
  708.         if (!$this->options->getDebugKeepTemp()) {
  709.             Cache::clear($this->options->getDebugPng());
  710.         }
  711.         global $_dompdf_warnings$_dompdf_show_warnings;
  712.         if ($_dompdf_show_warnings && isset($_dompdf_warnings)) {
  713.             echo '<b>Dompdf Warnings</b><br><pre>';
  714.             foreach ($_dompdf_warnings as $msg) {
  715.                 echo $msg "\n";
  716.             }
  717.             if ($canvas instanceof CPDF) {
  718.                 echo $canvas->get_cpdf()->messages;
  719.             }
  720.             echo '</pre>';
  721.             flush();
  722.         }
  723.         if ($logOutputFile && is_writable($logOutputFile)) {
  724.             $this->writeLog($logOutputFile$startTime);
  725.             ob_end_clean();
  726.         }
  727.         $this->restorePhpConfig();
  728.     }
  729.     /**
  730.      * Add meta information to the PDF after rendering
  731.      */
  732.     public function add_info($label$value)
  733.     {
  734.         $canvas $this->getCanvas();
  735.         if (!is_null($canvas)) {
  736.             $canvas->add_info($label$value);
  737.         }
  738.     }
  739.     /**
  740.      * Writes the output buffer in the log file
  741.      *
  742.      * @param string $logOutputFile
  743.      * @param float $startTime
  744.      */
  745.     private function writeLog(string $logOutputFilefloat $startTime): void
  746.     {
  747.         $frames Frame::$ID_COUNTER;
  748.         $memory memory_get_peak_usage(true) / 1024;
  749.         $time = (microtime(true) - $startTime) * 1000;
  750.         $out sprintf(
  751.             "<span style='color: #000' title='Frames'>%6d</span>" .
  752.             "<span style='color: #009' title='Memory'>%10.2f KB</span>" .
  753.             "<span style='color: #900' title='Time'>%10.2f ms</span>" .
  754.             "<span  title='Quirksmode'>  " .
  755.             ($this->quirksmode "<span style='color: #d00'> ON</span>" "<span style='color: #0d0'>OFF</span>") .
  756.             "</span><br />"$frames$memory$time);
  757.         $out .= ob_get_contents();
  758.         ob_clean();
  759.         file_put_contents($logOutputFile$out);
  760.     }
  761.     /**
  762.      * Streams the PDF to the client.
  763.      *
  764.      * The file will open a download dialog by default. The options
  765.      * parameter controls the output. Accepted options (array keys) are:
  766.      *
  767.      * 'compress' = > 1 (=default) or 0:
  768.      *   Apply content stream compression
  769.      *
  770.      * 'Attachment' => 1 (=default) or 0:
  771.      *   Set the 'Content-Disposition:' HTTP header to 'attachment'
  772.      *   (thereby causing the browser to open a download dialog)
  773.      *
  774.      * @param string $filename the name of the streamed file
  775.      * @param array $options header options (see above)
  776.      */
  777.     public function stream($filename "document.pdf"$options = [])
  778.     {
  779.         $this->setPhpConfig();
  780.         $canvas $this->getCanvas();
  781.         if (!is_null($canvas)) {
  782.             $canvas->stream($filename$options);
  783.         }
  784.         $this->restorePhpConfig();
  785.     }
  786.     /**
  787.      * Returns the PDF as a string.
  788.      *
  789.      * The options parameter controls the output. Accepted options are:
  790.      *
  791.      * 'compress' = > 1 or 0 - apply content stream compression, this is
  792.      *    on (1) by default
  793.      *
  794.      * @param array $options options (see above)
  795.      *
  796.      * @return string|null
  797.      */
  798.     public function output($options = [])
  799.     {
  800.         $this->setPhpConfig();
  801.         $canvas $this->getCanvas();
  802.         if (is_null($canvas)) {
  803.             return null;
  804.         }
  805.         $output $canvas->output($options);
  806.         $this->restorePhpConfig();
  807.         return $output;
  808.     }
  809.     /**
  810.      * @return string
  811.      * @deprecated
  812.      */
  813.     public function output_html()
  814.     {
  815.         return $this->outputHtml();
  816.     }
  817.     /**
  818.      * Returns the underlying HTML document as a string
  819.      *
  820.      * @return string
  821.      */
  822.     public function outputHtml()
  823.     {
  824.         return $this->dom->saveHTML();
  825.     }
  826.     /**
  827.      * Get the dompdf option value
  828.      *
  829.      * @param string $key
  830.      * @return mixed
  831.      * @deprecated
  832.      */
  833.     public function get_option($key)
  834.     {
  835.         return $this->options->get($key);
  836.     }
  837.     /**
  838.      * @param string $key
  839.      * @param mixed $value
  840.      * @return $this
  841.      * @deprecated
  842.      */
  843.     public function set_option($key$value)
  844.     {
  845.         $this->options->set($key$value);
  846.         return $this;
  847.     }
  848.     /**
  849.      * @param array $options
  850.      * @return $this
  851.      * @deprecated
  852.      */
  853.     public function set_options(array $options)
  854.     {
  855.         $this->options->set($options);
  856.         return $this;
  857.     }
  858.     /**
  859.      * @param string $size
  860.      * @param string $orientation
  861.      * @deprecated
  862.      */
  863.     public function set_paper($size$orientation "portrait")
  864.     {
  865.         $this->setPaper($size$orientation);
  866.     }
  867.     /**
  868.      * Sets the paper size & orientation
  869.      *
  870.      * @param string|array $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}
  871.      * @param string $orientation 'portrait' or 'landscape'
  872.      * @return $this
  873.      */
  874.     public function setPaper($size$orientation "portrait")
  875.     {
  876.         $this->paperSize $size;
  877.         $this->paperOrientation $orientation;
  878.         return $this;
  879.     }
  880.     /**
  881.      * Gets the paper size
  882.      *
  883.      * @param null|string|array $paperSize
  884.      * @return int[] A four-element integer array
  885.      */
  886.     public function getPaperSize($paperSize null)
  887.     {
  888.         $size $paperSize !== null $paperSize $this->paperSize;
  889.         if (is_array($size)) {
  890.             return $size;
  891.         } else if (isset(Adapter\CPDF::$PAPER_SIZES[mb_strtolower($size)])) {
  892.             return Adapter\CPDF::$PAPER_SIZES[mb_strtolower($size)];
  893.         } else {
  894.             return Adapter\CPDF::$PAPER_SIZES["letter"];
  895.         }
  896.     }
  897.     /**
  898.      * Gets the paper orientation
  899.      *
  900.      * @return string Either "portrait" or "landscape"
  901.      */
  902.     public function getPaperOrientation()
  903.     {
  904.         return $this->paperOrientation;
  905.     }
  906.     /**
  907.      * @param FrameTree $tree
  908.      * @return $this
  909.      */
  910.     public function setTree(FrameTree $tree)
  911.     {
  912.         $this->tree $tree;
  913.         return $this;
  914.     }
  915.     /**
  916.      * @return FrameTree
  917.      * @deprecated
  918.      */
  919.     public function get_tree()
  920.     {
  921.         return $this->getTree();
  922.     }
  923.     /**
  924.      * Returns the underlying {@link FrameTree} object
  925.      *
  926.      * @return FrameTree
  927.      */
  928.     public function getTree()
  929.     {
  930.         return $this->tree;
  931.     }
  932.     /**
  933.      * @param string $protocol
  934.      * @return $this
  935.      * @deprecated
  936.      */
  937.     public function set_protocol($protocol)
  938.     {
  939.         return $this->setProtocol($protocol);
  940.     }
  941.     /**
  942.      * Sets the protocol to use
  943.      * FIXME validate these
  944.      *
  945.      * @param string $protocol
  946.      * @return $this
  947.      */
  948.     public function setProtocol(string $protocol)
  949.     {
  950.         $this->protocol $protocol;
  951.         return $this;
  952.     }
  953.     /**
  954.      * @return string
  955.      * @deprecated
  956.      */
  957.     public function get_protocol()
  958.     {
  959.         return $this->getProtocol();
  960.     }
  961.     /**
  962.      * Returns the protocol in use
  963.      *
  964.      * @return string
  965.      */
  966.     public function getProtocol()
  967.     {
  968.         return $this->protocol;
  969.     }
  970.     /**
  971.      * @param string $host
  972.      * @deprecated
  973.      */
  974.     public function set_host($host)
  975.     {
  976.         $this->setBaseHost($host);
  977.     }
  978.     /**
  979.      * Sets the base hostname
  980.      *
  981.      * @param string $baseHost
  982.      * @return $this
  983.      */
  984.     public function setBaseHost(string $baseHost)
  985.     {
  986.         $this->baseHost $baseHost;
  987.         return $this;
  988.     }
  989.     /**
  990.      * @return string
  991.      * @deprecated
  992.      */
  993.     public function get_host()
  994.     {
  995.         return $this->getBaseHost();
  996.     }
  997.     /**
  998.      * Returns the base hostname
  999.      *
  1000.      * @return string
  1001.      */
  1002.     public function getBaseHost()
  1003.     {
  1004.         return $this->baseHost;
  1005.     }
  1006.     /**
  1007.      * Sets the base path
  1008.      *
  1009.      * @param string $path
  1010.      * @deprecated
  1011.      */
  1012.     public function set_base_path($path)
  1013.     {
  1014.         $this->setBasePath($path);
  1015.     }
  1016.     /**
  1017.      * Sets the base path
  1018.      *
  1019.      * @param string $basePath
  1020.      * @return $this
  1021.      */
  1022.     public function setBasePath(string $basePath)
  1023.     {
  1024.         $this->basePath $basePath;
  1025.         return $this;
  1026.     }
  1027.     /**
  1028.      * @return string
  1029.      * @deprecated
  1030.      */
  1031.     public function get_base_path()
  1032.     {
  1033.         return $this->getBasePath();
  1034.     }
  1035.     /**
  1036.      * Returns the base path
  1037.      *
  1038.      * @return string
  1039.      */
  1040.     public function getBasePath()
  1041.     {
  1042.         return $this->basePath;
  1043.     }
  1044.     /**
  1045.      * @param string $default_view The default document view
  1046.      * @param array $options The view's options
  1047.      * @return $this
  1048.      * @deprecated
  1049.      */
  1050.     public function set_default_view($default_view$options)
  1051.     {
  1052.         return $this->setDefaultView($default_view$options);
  1053.     }
  1054.     /**
  1055.      * Sets the default view
  1056.      *
  1057.      * @param string $defaultView The default document view
  1058.      * @param array $options The view's options
  1059.      * @return $this
  1060.      */
  1061.     public function setDefaultView($defaultView$options)
  1062.     {
  1063.         $this->defaultView $defaultView;
  1064.         $this->defaultViewOptions $options;
  1065.         return $this;
  1066.     }
  1067.     /**
  1068.      * @param resource $http_context
  1069.      * @return $this
  1070.      * @deprecated
  1071.      */
  1072.     public function set_http_context($http_context)
  1073.     {
  1074.         return $this->setHttpContext($http_context);
  1075.     }
  1076.     /**
  1077.      * Sets the HTTP context
  1078.      *
  1079.      * @param resource|array $httpContext
  1080.      * @return $this
  1081.      */
  1082.     public function setHttpContext($httpContext)
  1083.     {
  1084.         $this->options->setHttpContext($httpContext);
  1085.         return $this;
  1086.     }
  1087.     /**
  1088.      * @return resource
  1089.      * @deprecated
  1090.      */
  1091.     public function get_http_context()
  1092.     {
  1093.         return $this->getHttpContext();
  1094.     }
  1095.     /**
  1096.      * Returns the HTTP context
  1097.      *
  1098.      * @return resource
  1099.      */
  1100.     public function getHttpContext()
  1101.     {
  1102.         return $this->options->getHttpContext();
  1103.     }
  1104.     /**
  1105.      * @param Canvas $canvas
  1106.      * @return $this
  1107.      */
  1108.     public function setCanvas(Canvas $canvas)
  1109.     {
  1110.         $this->canvas $canvas;
  1111.         return $this;
  1112.     }
  1113.     /**
  1114.      * @return Canvas
  1115.      * @deprecated
  1116.      */
  1117.     public function get_canvas()
  1118.     {
  1119.         return $this->getCanvas();
  1120.     }
  1121.     /**
  1122.      * Return the underlying Canvas instance (e.g. Dompdf\Adapter\CPDF, Dompdf\Adapter\GD)
  1123.      *
  1124.      * @return Canvas
  1125.      */
  1126.     public function getCanvas()
  1127.     {
  1128.         return $this->canvas;
  1129.     }
  1130.     /**
  1131.      * @param Stylesheet $css
  1132.      * @return $this
  1133.      */
  1134.     public function setCss(Stylesheet $css)
  1135.     {
  1136.         $this->css $css;
  1137.         return $this;
  1138.     }
  1139.     /**
  1140.      * @return Stylesheet
  1141.      * @deprecated
  1142.      */
  1143.     public function get_css()
  1144.     {
  1145.         return $this->getCss();
  1146.     }
  1147.     /**
  1148.      * Returns the stylesheet
  1149.      *
  1150.      * @return Stylesheet
  1151.      */
  1152.     public function getCss()
  1153.     {
  1154.         return $this->css;
  1155.     }
  1156.     /**
  1157.      * @param DOMDocument $dom
  1158.      * @return $this
  1159.      */
  1160.     public function setDom(DOMDocument $dom)
  1161.     {
  1162.         $this->dom $dom;
  1163.         return $this;
  1164.     }
  1165.     /**
  1166.      * @return DOMDocument
  1167.      * @deprecated
  1168.      */
  1169.     public function get_dom()
  1170.     {
  1171.         return $this->getDom();
  1172.     }
  1173.     /**
  1174.      * @return DOMDocument
  1175.      */
  1176.     public function getDom()
  1177.     {
  1178.         return $this->dom;
  1179.     }
  1180.     /**
  1181.      * @param Options $options
  1182.      * @return $this
  1183.      */
  1184.     public function setOptions(Options $options)
  1185.     {
  1186.         // For backwards compatibility
  1187.         if ($this->options && $this->options->getHttpContext() && !$options->getHttpContext()) {
  1188.             $options->setHttpContext($this->options->getHttpContext());
  1189.         }
  1190.         $this->options $options;
  1191.         $fontMetrics $this->getFontMetrics();
  1192.         if (isset($fontMetrics)) {
  1193.             $fontMetrics->setOptions($options);
  1194.         }
  1195.         return $this;
  1196.     }
  1197.     /**
  1198.      * @return Options
  1199.      */
  1200.     public function getOptions()
  1201.     {
  1202.         return $this->options;
  1203.     }
  1204.     /**
  1205.      * @return array
  1206.      * @deprecated
  1207.      */
  1208.     public function get_callbacks()
  1209.     {
  1210.         return $this->getCallbacks();
  1211.     }
  1212.     /**
  1213.      * Returns the callbacks array
  1214.      *
  1215.      * @return array
  1216.      */
  1217.     public function getCallbacks()
  1218.     {
  1219.         return $this->callbacks;
  1220.     }
  1221.     /**
  1222.      * @param array $callbacks the set of callbacks to set
  1223.      * @deprecated
  1224.      */
  1225.     public function set_callbacks($callbacks)
  1226.     {
  1227.         $this->setCallbacks($callbacks);
  1228.     }
  1229.     /**
  1230.      * Sets callbacks for events like rendering of pages and elements.
  1231.      *
  1232.      * The callbacks array should contain arrays with `event` set to a callback
  1233.      * event name and `f` set to a function or any other callable.
  1234.      *
  1235.      * The available callback events are:
  1236.      * * `begin_page_reflow`: called before page reflow
  1237.      * * `begin_frame`: called before a frame is rendered
  1238.      * * `end_frame`: called after frame rendering is complete
  1239.      * * `begin_page_render`: called before a page is rendered
  1240.      * * `end_page_render`: called after page rendering is complete
  1241.      *
  1242.      * The function `f` must take an array as argument, which contains info
  1243.      * about the event (`[0 => Canvas, 1 => Frame, "canvas" => Canvas,
  1244.      * "frame" => Frame]`).
  1245.      *
  1246.      * @param array $callbacks The set of callbacks to set
  1247.      */
  1248.     public function setCallbacks($callbacks)
  1249.     {
  1250.         if (is_array($callbacks)) {
  1251.             $this->callbacks = [];
  1252.             foreach ($callbacks as $c) {
  1253.                 if (is_array($c) && isset($c["event"]) && isset($c["f"])) {
  1254.                     $event $c["event"];
  1255.                     $f $c["f"];
  1256.                     if (is_string($event) && is_callable($f)) {
  1257.                         $this->callbacks[$event][] = $f;
  1258.                     }
  1259.                 }
  1260.             }
  1261.         }
  1262.     }
  1263.     /**
  1264.      * @return boolean
  1265.      * @deprecated
  1266.      */
  1267.     public function get_quirksmode()
  1268.     {
  1269.         return $this->getQuirksmode();
  1270.     }
  1271.     /**
  1272.      * Get the quirks mode
  1273.      *
  1274.      * @return boolean true if quirks mode is active
  1275.      */
  1276.     public function getQuirksmode()
  1277.     {
  1278.         return $this->quirksmode;
  1279.     }
  1280.     /**
  1281.      * @param FontMetrics $fontMetrics
  1282.      * @return $this
  1283.      */
  1284.     public function setFontMetrics(FontMetrics $fontMetrics)
  1285.     {
  1286.         $this->fontMetrics $fontMetrics;
  1287.         return $this;
  1288.     }
  1289.     /**
  1290.      * @return FontMetrics
  1291.      */
  1292.     public function getFontMetrics()
  1293.     {
  1294.         return $this->fontMetrics;
  1295.     }
  1296.     /**
  1297.      * PHP5 overloaded getter
  1298.      * Along with {@link Dompdf::__set()} __get() provides access to all
  1299.      * properties directly.  Typically __get() is not called directly outside
  1300.      * of this class.
  1301.      *
  1302.      * @param string $prop
  1303.      *
  1304.      * @throws Exception
  1305.      * @return mixed
  1306.      */
  1307.     function __get($prop)
  1308.     {
  1309.         switch ($prop) {
  1310.             case 'version':
  1311.                 return $this->version;
  1312.             default:
  1313.                 throw new Exception('Invalid property: ' $prop);
  1314.         }
  1315.     }
  1316. }