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

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