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

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