-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-// Tags for FIX ME!!!: (in order of priority)
-// XXX - should be fixed NAO!
-// XERROR - with regards to parse errors
-// XSCRIPT - with regards to scripting mode
-// XENCODING - with regards to encoding (for reparsing tests)
-// XDOM - DOM specific code (tagName is explicitly not marked).
-// this is not (yet) in helper functions.
-
-class HTML5_TreeBuilder {
- public $stack = array();
- public $content_model;
-
- private $mode;
- private $original_mode;
- private $secondary_mode;
- private $dom;
- // Whether or not normal insertion of nodes should actually foster
- // parent (used in one case in spec)
- private $foster_parent = false;
- private $a_formatting = array();
-
- private $head_pointer = null;
- private $form_pointer = null;
-
- private $flag_frameset_ok = true;
- private $flag_force_quirks = false;
- private $ignored = false;
- private $quirks_mode = null;
- // this gets to 2 when we want to ignore the next lf character, and
- // is decrement at the beginning of each processed token (this way,
- // code can check for (bool)$ignore_lf_token, but it phases out
- // appropriately)
- private $ignore_lf_token = 0;
- private $fragment = false;
- private $root;
-
- private $scoping = array('applet','button','caption','html','marquee','object','table','td','th', 'svg:foreignObject');
- private $formatting = array('a','b','big','code','em','font','i','nobr','s','small','strike','strong','tt','u');
- // dl and ds are speculative
- private $special = array('address','area','article','aside','base','basefont','bgsound',
- 'blockquote','body','br','center','col','colgroup','command','dc','dd','details','dir','div','dl','ds',
- 'dt','embed','fieldset','figure','footer','form','frame','frameset','h1','h2','h3','h4','h5',
- 'h6','head','header','hgroup','hr','iframe','img','input','isindex','li','link',
- 'listing','menu','meta','nav','noembed','noframes','noscript','ol',
- 'p','param','plaintext','pre','script','select','spacer','style',
- 'tbody','textarea','tfoot','thead','title','tr','ul','wbr');
-
- private $pendingTableCharacters;
- private $pendingTableCharactersDirty;
-
- // Tree construction modes
- const INITIAL = 0;
- const BEFORE_HTML = 1;
- const BEFORE_HEAD = 2;
- const IN_HEAD = 3;
- const IN_HEAD_NOSCRIPT = 4;
- const AFTER_HEAD = 5;
- const IN_BODY = 6;
- const IN_CDATA_RCDATA = 7;
- const IN_TABLE = 8;
- const IN_TABLE_TEXT = 9;
- const IN_CAPTION = 10;
- const IN_COLUMN_GROUP = 11;
- const IN_TABLE_BODY = 12;
- const IN_ROW = 13;
- const IN_CELL = 14;
- const IN_SELECT = 15;
- const IN_SELECT_IN_TABLE= 16;
- const IN_FOREIGN_CONTENT= 17;
- const AFTER_BODY = 18;
- const IN_FRAMESET = 19;
- const AFTER_FRAMESET = 20;
- const AFTER_AFTER_BODY = 21;
- const AFTER_AFTER_FRAMESET = 22;
-
- /**
- * Converts a magic number to a readable name. Use for debugging.
- */
- private function strConst($number) {
- static $lookup;
- if (!$lookup) {
- $lookup = array();
- $r = new ReflectionClass('HTML5_TreeBuilder');
- $consts = $r->getConstants();
- foreach ($consts as $const => $num) {
- if (!is_int($num)) {
- continue;
- }
- $lookup[$num] = $const;
- }
- }
- return $lookup[$number];
- }
-
- // The different types of elements.
- const SPECIAL = 100;
- const SCOPING = 101;
- const FORMATTING = 102;
- const PHRASING = 103;
-
- // Quirks modes in $quirks_mode
- const NO_QUIRKS = 200;
- const QUIRKS_MODE = 201;
- const LIMITED_QUIRKS_MODE = 202;
-
- // Marker to be placed in $a_formatting
- const MARKER = 300;
-
- // Namespaces for foreign content
- const NS_HTML = null; // to prevent DOM from requiring NS on everything
- const NS_MATHML = 'http://www.w3.org/1998/Math/MathML';
- const NS_SVG = 'http://www.w3.org/2000/svg';
- const NS_XLINK = 'http://www.w3.org/1999/xlink';
- const NS_XML = 'http://www.w3.org/XML/1998/namespace';
- const NS_XMLNS = 'http://www.w3.org/2000/xmlns/';
-
- // Different types of scopes to test for elements
- const SCOPE = 0;
- const SCOPE_LISTITEM = 1;
- const SCOPE_TABLE = 2;
-
- /**
- * HTML5_TreeBuilder constructor.
- */
- public function __construct() {
- $this->mode = self::INITIAL;
- $this->dom = new DOMDocument;
-
- $this->dom->encoding = 'UTF-8';
- $this->dom->preserveWhiteSpace = true;
- $this->dom->substituteEntities = true;
- $this->dom->strictErrorChecking = false;
- }
-
- public function getQuirksMode(){
- return $this->quirks_mode;
- }
-
- /**
- * Process tag tokens
- *
- * @param $token
- * @param null $mode
- */
- public function emitToken($token, $mode = null) {
- // XXX: ignore parse errors... why are we emitting them, again?
- if ($token['type'] === HTML5_Tokenizer::PARSEERROR) {
- return;
- }
- if ($mode === null) {
- $mode = $this->mode;
- }
-
- /*
- $backtrace = debug_backtrace();
- if ($backtrace[1]['class'] !== 'HTML5_TreeBuilder') echo "--\n";
- echo $this->strConst($mode);
- if ($this->original_mode) echo " (originally ".$this->strConst($this->original_mode).")";
- echo "\n ";
- token_dump($token);
- $this->printStack();
- $this->printActiveFormattingElements();
- if ($this->foster_parent) echo " -> this is a foster parent mode\n";
- if ($this->flag_frameset_ok) echo " -> frameset ok\n";
- */
-
- if ($this->ignore_lf_token) {
- $this->ignore_lf_token--;
- }
- $this->ignored = false;
-
- switch ($mode) {
- case self::INITIAL:
-
- /* A character token that is one of U+0009 CHARACTER TABULATION,
- * U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020 SPACE */
- if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Ignore the token. */
- $this->ignored = true;
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- if (
- $token['name'] !== 'html' || !empty($token['public']) ||
- !empty($token['system']) || $token !== 'about:legacy-compat'
- ) {
- /* If the DOCTYPE token's name is not a case-sensitive match
- * for the string "html", or if the token's public identifier
- * is not missing, or if the token's system identifier is
- * neither missing nor a case-sensitive match for the string
- * "about:legacy-compat", then there is a parse error (this
- * is the DOCTYPE parse error). */
- // DOCTYPE parse error
- }
- /* Append a DocumentType node to the Document node, with the name
- * attribute set to the name given in the DOCTYPE token, or the
- * empty string if the name was missing; the publicId attribute
- * set to the public identifier given in the DOCTYPE token, or
- * the empty string if the public identifier was missing; the
- * systemId attribute set to the system identifier given in the
- * DOCTYPE token, or the empty string if the system identifier
- * was missing; and the other attributes specific to
- * DocumentType objects set to null and empty lists as
- * appropriate. Associate the DocumentType node with the
- * Document object so that it is returned as the value of the
- * doctype attribute of the Document object. */
- if (!isset($token['public'])) {
- $token['public'] = null;
- }
- if (!isset($token['system'])) {
- $token['system'] = null;
- }
- // XDOM
- // Yes this is hacky. I'm kind of annoyed that I can't appendChild
- // a doctype to DOMDocument. Maybe I haven't chanted the right
- // syllables.
- $impl = new DOMImplementation();
- // This call can fail for particularly pathological cases (namely,
- // the qualifiedName parameter ($token['name']) could be missing.
- if ($token['name']) {
- $doctype = $impl->createDocumentType($token['name'], $token['public'], $token['system']);
- $this->dom->appendChild($doctype);
- } else {
- // It looks like libxml's not actually *able* to express this case.
- // So... don't.
- $this->dom->emptyDoctype = true;
- }
- $public = is_null($token['public']) ? false : strtolower($token['public']);
- $system = is_null($token['system']) ? false : strtolower($token['system']);
- $publicStartsWithForQuirks = array(
- "+//silmaril//dtd html pro v0r11 19970101//",
- "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
- "-//as//dtd html 3.0 aswedit + extensions//",
- "-//ietf//dtd html 2.0 level 1//",
- "-//ietf//dtd html 2.0 level 2//",
- "-//ietf//dtd html 2.0 strict level 1//",
- "-//ietf//dtd html 2.0 strict level 2//",
- "-//ietf//dtd html 2.0 strict//",
- "-//ietf//dtd html 2.0//",
- "-//ietf//dtd html 2.1e//",
- "-//ietf//dtd html 3.0//",
- "-//ietf//dtd html 3.2 final//",
- "-//ietf//dtd html 3.2//",
- "-//ietf//dtd html 3//",
- "-//ietf//dtd html level 0//",
- "-//ietf//dtd html level 1//",
- "-//ietf//dtd html level 2//",
- "-//ietf//dtd html level 3//",
- "-//ietf//dtd html strict level 0//",
- "-//ietf//dtd html strict level 1//",
- "-//ietf//dtd html strict level 2//",
- "-//ietf//dtd html strict level 3//",
- "-//ietf//dtd html strict//",
- "-//ietf//dtd html//",
- "-//metrius//dtd metrius presentational//",
- "-//microsoft//dtd internet explorer 2.0 html strict//",
- "-//microsoft//dtd internet explorer 2.0 html//",
- "-//microsoft//dtd internet explorer 2.0 tables//",
- "-//microsoft//dtd internet explorer 3.0 html strict//",
- "-//microsoft//dtd internet explorer 3.0 html//",
- "-//microsoft//dtd internet explorer 3.0 tables//",
- "-//netscape comm. corp.//dtd html//",
- "-//netscape comm. corp.//dtd strict html//",
- "-//o'reilly and associates//dtd html 2.0//",
- "-//o'reilly and associates//dtd html extended 1.0//",
- "-//o'reilly and associates//dtd html extended relaxed 1.0//",
- "-//spyglass//dtd html 2.0 extended//",
- "-//sq//dtd html 2.0 hotmetal + extensions//",
- "-//sun microsystems corp.//dtd hotjava html//",
- "-//sun microsystems corp.//dtd hotjava strict html//",
- "-//w3c//dtd html 3 1995-03-24//",
- "-//w3c//dtd html 3.2 draft//",
- "-//w3c//dtd html 3.2 final//",
- "-//w3c//dtd html 3.2//",
- "-//w3c//dtd html 3.2s draft//",
- "-//w3c//dtd html 4.0 frameset//",
- "-//w3c//dtd html 4.0 transitional//",
- "-//w3c//dtd html experimental 19960712//",
- "-//w3c//dtd html experimental 970421//",
- "-//w3c//dtd w3 html//",
- "-//w3o//dtd w3 html 3.0//",
- "-//webtechs//dtd mozilla html 2.0//",
- "-//webtechs//dtd mozilla html//",
- );
- $publicSetToForQuirks = array(
- "-//w3o//dtd w3 html strict 3.0//",
- "-/w3c/dtd html 4.0 transitional/en",
- "html",
- );
- $publicStartsWithAndSystemForQuirks = array(
- "-//w3c//dtd html 4.01 frameset//",
- "-//w3c//dtd html 4.01 transitional//",
- );
- $publicStartsWithForLimitedQuirks = array(
- "-//w3c//dtd xhtml 1.0 frameset//",
- "-//w3c//dtd xhtml 1.0 transitional//",
- );
- $publicStartsWithAndSystemForLimitedQuirks = array(
- "-//w3c//dtd html 4.01 frameset//",
- "-//w3c//dtd html 4.01 transitional//",
- );
- // first, do easy checks
- if (
- !empty($token['force-quirks']) ||
- strtolower($token['name']) !== 'html'
- ) {
- $this->quirks_mode = self::QUIRKS_MODE;
- } else {
- do {
- if ($system) {
- foreach ($publicStartsWithAndSystemForQuirks as $x) {
- if (strncmp($public, $x, strlen($x)) === 0) {
- $this->quirks_mode = self::QUIRKS_MODE;
- break;
- }
- }
- if (!is_null($this->quirks_mode)) {
- break;
- }
- foreach ($publicStartsWithAndSystemForLimitedQuirks as $x) {
- if (strncmp($public, $x, strlen($x)) === 0) {
- $this->quirks_mode = self::LIMITED_QUIRKS_MODE;
- break;
- }
- }
- if (!is_null($this->quirks_mode)) {
- break;
- }
- }
- foreach ($publicSetToForQuirks as $x) {
- if ($public === $x) {
- $this->quirks_mode = self::QUIRKS_MODE;
- break;
- }
- }
- if (!is_null($this->quirks_mode)) {
- break;
- }
- foreach ($publicStartsWithForLimitedQuirks as $x) {
- if (strncmp($public, $x, strlen($x)) === 0) {
- $this->quirks_mode = self::LIMITED_QUIRKS_MODE;
- }
- }
- if (!is_null($this->quirks_mode)) {
- break;
- }
- if ($system === "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
- $this->quirks_mode = self::QUIRKS_MODE;
- break;
- }
- foreach ($publicStartsWithForQuirks as $x) {
- if (strncmp($public, $x, strlen($x)) === 0) {
- $this->quirks_mode = self::QUIRKS_MODE;
- break;
- }
- }
- if (is_null($this->quirks_mode)) {
- $this->quirks_mode = self::NO_QUIRKS;
- }
- } while (false);
- }
- $this->mode = self::BEFORE_HTML;
- } else {
- // parse error
- /* Switch the insertion mode to "before html", then reprocess the
- * current token. */
- $this->mode = self::BEFORE_HTML;
- $this->quirks_mode = self::QUIRKS_MODE;
- $this->emitToken($token);
- }
- break;
-
- case self::BEFORE_HTML:
- /* A DOCTYPE token */
- if ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // Parse error. Ignore the token.
- $this->ignored = true;
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the Document object with the data
- attribute set to the data given in the comment token. */
- // XDOM
- $comment = $this->dom->createComment($token['data']);
- $this->dom->appendChild($comment);
-
- /* A character token that is one of one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- or U+0020 SPACE */
- } elseif ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Ignore the token. */
- $this->ignored = true;
-
- /* A start tag whose tag name is "html" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] == 'html') {
- /* Create an element for the token in the HTML namespace. Append it
- * to the Document object. Put this element in the stack of open
- * elements. */
- // XDOM
- $html = $this->insertElement($token, false);
- $this->dom->appendChild($html);
- $this->stack[] = $html;
-
- $this->mode = self::BEFORE_HEAD;
-
- } else {
- /* Create an html element. Append it to the Document object. Put
- * this element in the stack of open elements. */
- // XDOM
- $html = $this->dom->createElementNS(self::NS_HTML, 'html');
- $this->dom->appendChild($html);
- $this->stack[] = $html;
-
- /* Switch the insertion mode to "before head", then reprocess the
- * current token. */
- $this->mode = self::BEFORE_HEAD;
- $this->emitToken($token);
- }
- break;
-
- case self::BEFORE_HEAD:
- /* A character token that is one of one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- or U+0020 SPACE */
- if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Ignore the token. */
- $this->ignored = true;
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the current node with the data attribute
- set to the data given in the comment token. */
- $this->insertComment($token['data']);
-
- /* A DOCTYPE token */
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- /* Parse error. Ignore the token */
- $this->ignored = true;
- // parse error
-
- /* A start tag token with the tag name "html" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
- /* Process the token using the rules for the "in body"
- * insertion mode. */
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* A start tag token with the tag name "head" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') {
- /* Insert an HTML element for the token. */
- $element = $this->insertElement($token);
-
- /* Set the head element pointer to this new element node. */
- $this->head_pointer = $element;
-
- /* Change the insertion mode to "in head". */
- $this->mode = self::IN_HEAD;
-
- /* An end tag whose tag name is one of: "head", "body", "html", "br" */
- } elseif (
- $token['type'] === HTML5_Tokenizer::ENDTAG && (
- $token['name'] === 'head' || $token['name'] === 'body' ||
- $token['name'] === 'html' || $token['name'] === 'br'
- )) {
- /* Act as if a start tag token with the tag name "head" and no
- * attributes had been seen, then reprocess the current token. */
- $this->emitToken(array(
- 'name' => 'head',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => array()
- ));
- $this->emitToken($token);
-
- /* Any other end tag */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
-
- } else {
- /* Act as if a start tag token with the tag name "head" and no
- * attributes had been seen, then reprocess the current token.
- * Note: This will result in an empty head element being
- * generated, with the current token being reprocessed in the
- * "after head" insertion mode. */
- $this->emitToken(array(
- 'name' => 'head',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => array()
- ));
- $this->emitToken($token);
- }
- break;
-
- case self::IN_HEAD:
- /* A character token that is one of one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- or U+0020 SPACE. */
- if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Insert the character into the current node. */
- $this->insertText($token['data']);
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the current node with the data attribute
- set to the data given in the comment token. */
- $this->insertComment($token['data']);
-
- /* A DOCTYPE token */
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
- // parse error
-
- /* A start tag whose tag name is "html" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'html') {
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* A start tag whose tag name is one of: "base", "command", "link" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- ($token['name'] === 'base' || $token['name'] === 'command' ||
- $token['name'] === 'link')) {
- /* Insert an HTML element for the token. Immediately pop the
- * current node off the stack of open elements. */
- $this->insertElement($token);
- array_pop($this->stack);
-
- // YYY: Acknowledge the token's self-closing flag, if it is set.
-
- /* A start tag whose tag name is "meta" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'meta') {
- /* Insert an HTML element for the token. Immediately pop the
- * current node off the stack of open elements. */
- $this->insertElement($token);
- array_pop($this->stack);
-
- // XERROR: Acknowledge the token's self-closing flag, if it is set.
-
- // XENCODING: If the element has a charset attribute, and its value is a
- // supported encoding, and the confidence is currently tentative,
- // then change the encoding to the encoding given by the value of
- // the charset attribute.
- //
- // Otherwise, if the element has a content attribute, and applying
- // the algorithm for extracting an encoding from a Content-Type to
- // its value returns a supported encoding encoding, and the
- // confidence is currently tentative, then change the encoding to
- // the encoding encoding.
-
- /* A start tag with the tag name "title" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'title') {
- $this->insertRCDATAElement($token);
-
- /* A start tag whose tag name is "noscript", if the scripting flag is enabled, or
- * A start tag whose tag name is one of: "noframes", "style" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- ($token['name'] === 'noscript' || $token['name'] === 'noframes' || $token['name'] === 'style')) {
- // XSCRIPT: Scripting flag not respected
- $this->insertCDATAElement($token);
-
- // XSCRIPT: Scripting flag disable not implemented
-
- /* A start tag with the tag name "script" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') {
- /* 1. Create an element for the token in the HTML namespace. */
- $node = $this->insertElement($token, false);
-
- /* 2. Mark the element as being "parser-inserted" */
- // Uhhh... XSCRIPT
-
- /* 3. If the parser was originally created for the HTML
- * fragment parsing algorithm, then mark the script element as
- * "already executed". (fragment case) */
- // ditto... XSCRIPT
-
- /* 4. Append the new element to the current node and push it onto
- * the stack of open elements. */
- end($this->stack)->appendChild($node);
- $this->stack[] = $node;
- // I guess we could squash these together
-
- /* 6. Let the original insertion mode be the current insertion mode. */
- $this->original_mode = $this->mode;
- /* 7. Switch the insertion mode to "in CDATA/RCDATA" */
- $this->mode = self::IN_CDATA_RCDATA;
- /* 5. Switch the tokeniser's content model flag to the CDATA state. */
- $this->content_model = HTML5_Tokenizer::CDATA;
-
- /* An end tag with the tag name "head" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'head') {
- /* Pop the current node (which will be the head element) off the stack of open elements. */
- array_pop($this->stack);
-
- /* Change the insertion mode to "after head". */
- $this->mode = self::AFTER_HEAD;
-
- // Slight logic inversion here to minimize duplication
- /* A start tag with the tag name "head". */
- /* An end tag whose tag name is not one of: "body", "html", "br" */
- } elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') ||
- ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] !== 'html' &&
- $token['name'] !== 'body' && $token['name'] !== 'br')) {
- // Parse error. Ignore the token.
- $this->ignored = true;
-
- /* Anything else */
- } else {
- /* Act as if an end tag token with the tag name "head" had been
- * seen, and reprocess the current token. */
- $this->emitToken(array(
- 'name' => 'head',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- /* Then, reprocess the current token. */
- $this->emitToken($token);
- }
- break;
-
- case self::IN_HEAD_NOSCRIPT:
- if ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // parse error
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
- $this->processWithRulesFor($token, self::IN_BODY);
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'noscript') {
- /* Pop the current node (which will be a noscript element) from the
- * stack of open elements; the new current node will be a head
- * element. */
- array_pop($this->stack);
- $this->mode = self::IN_HEAD;
- } elseif (
- ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) ||
- ($token['type'] === HTML5_Tokenizer::COMMENT) ||
- ($token['type'] === HTML5_Tokenizer::STARTTAG && (
- $token['name'] === 'link' || $token['name'] === 'meta' ||
- $token['name'] === 'noframes' || $token['name'] === 'style'))) {
- $this->processWithRulesFor($token, self::IN_HEAD);
- // inverted logic
- } elseif (
- ($token['type'] === HTML5_Tokenizer::STARTTAG && (
- $token['name'] === 'head' || $token['name'] === 'noscript')) ||
- ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] !== 'br')) {
- // parse error
- } else {
- // parse error
- $this->emitToken(array(
- 'type' => HTML5_Tokenizer::ENDTAG,
- 'name' => 'noscript',
- ));
- $this->emitToken($token);
- }
- break;
-
- case self::AFTER_HEAD:
- /* Handle the token as follows: */
-
- /* A character token that is one of one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- or U+0020 SPACE */
- if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Append the character to the current node. */
- $this->insertText($token['data']);
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the current node with the data attribute
- set to the data given in the comment token. */
- $this->insertComment($token['data']);
-
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // parse error
-
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* A start tag token with the tag name "body" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'body') {
- $this->insertElement($token);
-
- /* Set the frameset-ok flag to "not ok". */
- $this->flag_frameset_ok = false;
-
- /* Change the insertion mode to "in body". */
- $this->mode = self::IN_BODY;
-
- /* A start tag token with the tag name "frameset" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'frameset') {
- /* Insert a frameset element for the token. */
- $this->insertElement($token);
-
- /* Change the insertion mode to "in frameset". */
- $this->mode = self::IN_FRAMESET;
-
- /* A start tag token whose tag name is one of: "base", "link", "meta",
- "script", "style", "title" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
- array('base', 'link', 'meta', 'noframes', 'script', 'style', 'title'))) {
- // parse error
- /* Push the node pointed to by the head element pointer onto the
- * stack of open elements. */
- $this->stack[] = $this->head_pointer;
- $this->processWithRulesFor($token, self::IN_HEAD);
- array_splice($this->stack, array_search($this->head_pointer, $this->stack, true), 1);
-
- // inversion of specification
- } elseif (
- ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') ||
- ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] !== 'body' && $token['name'] !== 'html' &&
- $token['name'] !== 'br')) {
- // parse error
-
- /* Anything else */
- } else {
- $this->emitToken(array(
- 'name' => 'body',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => array()
- ));
- $this->flag_frameset_ok = true;
- $this->emitToken($token);
- }
- break;
-
- case self::IN_BODY:
- /* Handle the token as follows: */
-
- switch($token['type']) {
- /* A character token */
- case HTML5_Tokenizer::CHARACTER:
- case HTML5_Tokenizer::SPACECHARACTER:
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Append the token's character to the current node. */
- $this->insertText($token['data']);
-
- /* If the token is not one of U+0009 CHARACTER TABULATION,
- * U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020
- * SPACE, then set the frameset-ok flag to "not ok". */
- // i.e., if any of the characters is not whitespace
- if (strlen($token['data']) !== strspn($token['data'], HTML5_Tokenizer::WHITESPACE)) {
- $this->flag_frameset_ok = false;
- }
- break;
-
- /* A comment token */
- case HTML5_Tokenizer::COMMENT:
- /* Append a Comment node to the current node with the data
- attribute set to the data given in the comment token. */
- $this->insertComment($token['data']);
- break;
-
- case HTML5_Tokenizer::DOCTYPE:
- // parse error
- break;
-
- case HTML5_Tokenizer::EOF:
- // parse error
- break;
-
- case HTML5_Tokenizer::STARTTAG:
- switch($token['name']) {
- case 'html':
- // parse error
- /* For each attribute on the token, check to see if the
- * attribute is already present on the top element of the
- * stack of open elements. If it is not, add the attribute
- * and its corresponding value to that element. */
- foreach($token['attr'] as $attr) {
- if (!$this->stack[0]->hasAttribute($attr['name'])) {
- $this->stack[0]->setAttribute($attr['name'], $attr['value']);
- }
- }
- break;
-
- case 'base': case 'command': case 'link': case 'meta': case 'noframes':
- case 'script': case 'style': case 'title':
- /* Process the token as if the insertion mode had been "in
- head". */
- $this->processWithRulesFor($token, self::IN_HEAD);
- break;
-
- /* A start tag token with the tag name "body" */
- case 'body':
- /* Parse error. If the second element on the stack of open
- elements is not a body element, or, if the stack of open
- elements has only one node on it, then ignore the token.
- (fragment case) */
- if (count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') {
- $this->ignored = true;
- // Ignore
-
- /* Otherwise, for each attribute on the token, check to see
- if the attribute is already present on the body element (the
- second element) on the stack of open elements. If it is not,
- add the attribute and its corresponding value to that
- element. */
- } else {
- foreach($token['attr'] as $attr) {
- if (!$this->stack[1]->hasAttribute($attr['name'])) {
- $this->stack[1]->setAttribute($attr['name'], $attr['value']);
- }
- }
- }
- break;
-
- case 'frameset':
- // parse error
- /* If the second element on the stack of open elements is
- * not a body element, or, if the stack of open elements
- * has only one node on it, then ignore the token.
- * (fragment case) */
- if (count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') {
- $this->ignored = true;
- // Ignore
- } elseif (!$this->flag_frameset_ok) {
- $this->ignored = true;
- // Ignore
- } else {
- /* 1. Remove the second element on the stack of open
- * elements from its parent node, if it has one. */
- if ($this->stack[1]->parentNode) {
- $this->stack[1]->parentNode->removeChild($this->stack[1]);
- }
-
- /* 2. Pop all the nodes from the bottom of the stack of
- * open elements, from the current node up to the root
- * html element. */
- array_splice($this->stack, 1);
-
- $this->insertElement($token);
- $this->mode = self::IN_FRAMESET;
- }
- break;
-
- // in spec, there is a diversion here
-
- case 'address': case 'article': case 'aside': case 'blockquote':
- case 'center': case 'datagrid': case 'details': case 'dir':
- case 'div': case 'dl': case 'fieldset': case 'figure': case 'footer':
- case 'header': case 'hgroup': case 'menu': case 'nav':
- case 'ol': case 'p': case 'section': case 'ul':
- /* If the stack of open elements has a p element in scope,
- then act as if an end tag with the tag name p had been
- seen. */
- if ($this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
- break;
-
- /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4",
- "h5", "h6" */
- case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
- /* If the stack of open elements has a p element in scope,
- then act as if an end tag with the tag name p had been seen. */
- if ($this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* If the current node is an element whose tag name is one
- * of "h1", "h2", "h3", "h4", "h5", or "h6", then this is a
- * parse error; pop the current node off the stack of open
- * elements. */
- $peek = array_pop($this->stack);
- if (in_array($peek->tagName, array("h1", "h2", "h3", "h4", "h5", "h6"))) {
- // parse error
- } else {
- $this->stack[] = $peek;
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
- break;
-
- case 'pre': case 'listing':
- /* If the stack of open elements has a p element in scope,
- then act as if an end tag with the tag name p had been seen. */
- if ($this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
- $this->insertElement($token);
- /* If the next token is a U+000A LINE FEED (LF) character
- * token, then ignore that token and move on to the next
- * one. (Newlines at the start of pre blocks are ignored as
- * an authoring convenience.) */
- $this->ignore_lf_token = 2;
- $this->flag_frameset_ok = false;
- break;
-
- /* A start tag whose tag name is "form" */
- case 'form':
- /* If the form element pointer is not null, ignore the
- token with a parse error. */
- if ($this->form_pointer !== null) {
- $this->ignored = true;
- // Ignore.
-
- /* Otherwise: */
- } else {
- /* If the stack of open elements has a p element in
- scope, then act as if an end tag with the tag name p
- had been seen. */
- if ($this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token, and set the
- form element pointer to point to the element created. */
- $element = $this->insertElement($token);
- $this->form_pointer = $element;
- }
- break;
-
- // condensed specification
- case 'li': case 'dc': case 'dd': case 'ds': case 'dt':
- /* 1. Set the frameset-ok flag to "not ok". */
- $this->flag_frameset_ok = false;
-
- $stack_length = count($this->stack) - 1;
- for($n = $stack_length; 0 <= $n; $n--) {
- /* 2. Initialise node to be the current node (the
- bottommost node of the stack). */
- $stop = false;
- $node = $this->stack[$n];
- $cat = $this->getElementCategory($node);
-
- // for case 'li':
- /* 3. If node is an li element, then act as if an end
- * tag with the tag name "li" had been seen, then jump
- * to the last step. */
- // for case 'dc': case 'dd': case 'ds': case 'dt':
- /* If node is a dc, dd, ds or dt element, then act as if an end
- * tag with the same tag name as node had been seen, then
- * jump to the last step. */
- if (($token['name'] === 'li' && $node->tagName === 'li') ||
- ($token['name'] !== 'li' && ($node->tagName == 'dc' || $node->tagName === 'dd' || $node->tagName == 'ds' || $node->tagName === 'dt'))) { // limited conditional
- $this->emitToken(array(
- 'type' => HTML5_Tokenizer::ENDTAG,
- 'name' => $node->tagName,
- ));
- break;
- }
-
- /* 4. If node is not in the formatting category, and is
- not in the phrasing category, and is not an address,
- div or p element, then stop this algorithm. */
- if ($cat !== self::FORMATTING && $cat !== self::PHRASING &&
- $node->tagName !== 'address' && $node->tagName !== 'div' &&
- $node->tagName !== 'p') {
- break;
- }
-
- /* 5. Otherwise, set node to the previous entry in the
- * stack of open elements and return to step 2. */
- }
-
- /* 6. This is the last step. */
-
- /* If the stack of open elements has a p element in scope,
- then act as if an end tag with the tag name p had been
- seen. */
- if ($this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Finally, insert an HTML element with the same tag
- name as the token's. */
- $this->insertElement($token);
- break;
-
- /* A start tag token whose tag name is "plaintext" */
- case 'plaintext':
- /* If the stack of open elements has a p element in scope,
- then act as if an end tag with the tag name p had been
- seen. */
- if ($this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- $this->content_model = HTML5_Tokenizer::PLAINTEXT;
- break;
-
- // more diversions
-
- /* A start tag whose tag name is "a" */
- case 'a':
- /* If the list of active formatting elements contains
- an element whose tag name is "a" between the end of the
- list and the last marker on the list (or the start of
- the list if there is no marker on the list), then this
- is a parse error; act as if an end tag with the tag name
- "a" had been seen, then remove that element from the list
- of active formatting elements and the stack of open
- elements if the end tag didn't already remove it (it
- might not have if the element is not in table scope). */
- $leng = count($this->a_formatting);
-
- for ($n = $leng - 1; $n >= 0; $n--) {
- if ($this->a_formatting[$n] === self::MARKER) {
- break;
-
- } elseif ($this->a_formatting[$n]->tagName === 'a') {
- $a = $this->a_formatting[$n];
- $this->emitToken(array(
- 'name' => 'a',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- if (in_array($a, $this->a_formatting)) {
- $a_i = array_search($a, $this->a_formatting, true);
- if ($a_i !== false) {
- array_splice($this->a_formatting, $a_i, 1);
- }
- }
- if (in_array($a, $this->stack)) {
- $a_i = array_search($a, $this->stack, true);
- if ($a_i !== false) {
- array_splice($this->stack, $a_i, 1);
- }
- }
- break;
- }
- }
-
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Insert an HTML element for the token. */
- $el = $this->insertElement($token);
-
- /* Add that element to the list of active formatting
- elements. */
- $this->a_formatting[] = $el;
- break;
-
- case 'b': case 'big': case 'code': case 'em': case 'font': case 'i':
- case 's': case 'small': case 'strike':
- case 'strong': case 'tt': case 'u':
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Insert an HTML element for the token. */
- $el = $this->insertElement($token);
-
- /* Add that element to the list of active formatting
- elements. */
- $this->a_formatting[] = $el;
- break;
-
- case 'nobr':
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* If the stack of open elements has a nobr element in
- * scope, then this is a parse error; act as if an end tag
- * with the tag name "nobr" had been seen, then once again
- * reconstruct the active formatting elements, if any. */
- if ($this->elementInScope('nobr')) {
- $this->emitToken(array(
- 'name' => 'nobr',
- 'type' => HTML5_Tokenizer::ENDTAG,
- ));
- $this->reconstructActiveFormattingElements();
- }
-
- /* Insert an HTML element for the token. */
- $el = $this->insertElement($token);
-
- /* Add that element to the list of active formatting
- elements. */
- $this->a_formatting[] = $el;
- break;
-
- // another diversion
-
- /* A start tag token whose tag name is "button" */
- case 'button':
- /* If the stack of open elements has a button element in scope,
- then this is a parse error; act as if an end tag with the tag
- name "button" had been seen, then reprocess the token. (We don't
- do that. Unnecessary.) (I hope you're right! -- ezyang) */
- if ($this->elementInScope('button')) {
- $this->emitToken(array(
- 'name' => 'button',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* Insert a marker at the end of the list of active
- formatting elements. */
- $this->a_formatting[] = self::MARKER;
-
- $this->flag_frameset_ok = false;
- break;
-
- case 'applet': case 'marquee': case 'object':
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* Insert a marker at the end of the list of active
- formatting elements. */
- $this->a_formatting[] = self::MARKER;
-
- $this->flag_frameset_ok = false;
- break;
-
- // spec diversion
-
- /* A start tag whose tag name is "table" */
- case 'table':
- /* If the Document is not set to quirks mode, and the
- * stack of open elements has a p element in scope, then
- * act as if an end tag with the tag name "p" had been
- * seen. */
- if ($this->quirks_mode !== self::QUIRKS_MODE &&
- $this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- $this->flag_frameset_ok = false;
-
- /* Change the insertion mode to "in table". */
- $this->mode = self::IN_TABLE;
- break;
-
- /* A start tag whose tag name is one of: "area", "basefont",
- "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */
- case 'area': case 'basefont': case 'bgsound': case 'br':
- case 'embed': case 'img': case 'input': case 'keygen': case 'spacer':
- case 'wbr':
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* Immediately pop the current node off the stack of open elements. */
- array_pop($this->stack);
-
- // YYY: Acknowledge the token's self-closing flag, if it is set.
-
- $this->flag_frameset_ok = false;
- break;
-
- case 'param': case 'source':
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* Immediately pop the current node off the stack of open elements. */
- array_pop($this->stack);
-
- // YYY: Acknowledge the token's self-closing flag, if it is set.
- break;
-
- /* A start tag whose tag name is "hr" */
- case 'hr':
- /* If the stack of open elements has a p element in scope,
- then act as if an end tag with the tag name p had been seen. */
- if ($this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* Immediately pop the current node off the stack of open elements. */
- array_pop($this->stack);
-
- // YYY: Acknowledge the token's self-closing flag, if it is set.
-
- $this->flag_frameset_ok = false;
- break;
-
- /* A start tag whose tag name is "image" */
- case 'image':
- /* Parse error. Change the token's tag name to "img" and
- reprocess it. (Don't ask.) */
- $token['name'] = 'img';
- $this->emitToken($token);
- break;
-
- /* A start tag whose tag name is "isindex" */
- case 'isindex':
- /* Parse error. */
-
- /* If the form element pointer is not null,
- then ignore the token. */
- if ($this->form_pointer === null) {
- /* Act as if a start tag token with the tag name "form" had
- been seen. */
- /* If the token has an attribute called "action", set
- * the action attribute on the resulting form
- * element to the value of the "action" attribute of
- * the token. */
- $attr = array();
- $action = $this->getAttr($token, 'action');
- if ($action !== false) {
- $attr[] = array('name' => 'action', 'value' => $action);
- }
- $this->emitToken(array(
- 'name' => 'form',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => $attr
- ));
-
- /* Act as if a start tag token with the tag name "hr" had
- been seen. */
- $this->emitToken(array(
- 'name' => 'hr',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => array()
- ));
-
- /* Act as if a start tag token with the tag name "label"
- had been seen. */
- $this->emitToken(array(
- 'name' => 'label',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => array()
- ));
-
- /* Act as if a stream of character tokens had been seen. */
- $prompt = $this->getAttr($token, 'prompt');
- if ($prompt === false) {
- $prompt = 'This is a searchable index. '.
- 'Insert your search keywords here: ';
- }
- $this->emitToken(array(
- 'data' => $prompt,
- 'type' => HTML5_Tokenizer::CHARACTER,
- ));
-
- /* Act as if a start tag token with the tag name "input"
- had been seen, with all the attributes from the "isindex"
- token, except with the "name" attribute set to the value
- "isindex" (ignoring any explicit "name" attribute). */
- $attr = array();
- foreach ($token['attr'] as $keypair) {
- if ($keypair['name'] === 'name' || $keypair['name'] === 'action' ||
- $keypair['name'] === 'prompt') {
- continue;
- }
- $attr[] = $keypair;
- }
- $attr[] = array('name' => 'name', 'value' => 'isindex');
-
- $this->emitToken(array(
- 'name' => 'input',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => $attr
- ));
-
- /* Act as if an end tag token with the tag name "label"
- had been seen. */
- $this->emitToken(array(
- 'name' => 'label',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- /* Act as if a start tag token with the tag name "hr" had
- been seen. */
- $this->emitToken(array(
- 'name' => 'hr',
- 'type' => HTML5_Tokenizer::STARTTAG
- ));
-
- /* Act as if an end tag token with the tag name "form" had
- been seen. */
- $this->emitToken(array(
- 'name' => 'form',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- } else {
- $this->ignored = true;
- }
- break;
-
- /* A start tag whose tag name is "textarea" */
- case 'textarea':
- $this->insertElement($token);
-
- /* If the next token is a U+000A LINE FEED (LF)
- * character token, then ignore that token and move on to
- * the next one. (Newlines at the start of textarea
- * elements are ignored as an authoring convenience.)
- * need flag, see also */
- $this->ignore_lf_token = 2;
-
- $this->original_mode = $this->mode;
- $this->flag_frameset_ok = false;
- $this->mode = self::IN_CDATA_RCDATA;
-
- /* Switch the tokeniser's content model flag to the
- RCDATA state. */
- $this->content_model = HTML5_Tokenizer::RCDATA;
- break;
-
- /* A start tag token whose tag name is "xmp" */
- case 'xmp':
- /* If the stack of open elements has a p element in
- scope, then act as if an end tag with the tag name
- "p" has been seen. */
- if ($this->elementInScope('p')) {
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- $this->flag_frameset_ok = false;
-
- $this->insertCDATAElement($token);
- break;
-
- case 'iframe':
- $this->flag_frameset_ok = false;
- $this->insertCDATAElement($token);
- break;
-
- case 'noembed': case 'noscript':
- // XSCRIPT: should check scripting flag
- $this->insertCDATAElement($token);
- break;
-
- /* A start tag whose tag name is "select" */
- case 'select':
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- $this->flag_frameset_ok = false;
-
- /* If the insertion mode is one of in table", "in caption",
- * "in column group", "in table body", "in row", or "in
- * cell", then switch the insertion mode to "in select in
- * table". Otherwise, switch the insertion mode to "in
- * select". */
- if (
- $this->mode === self::IN_TABLE || $this->mode === self::IN_CAPTION ||
- $this->mode === self::IN_COLUMN_GROUP || $this->mode ==+self::IN_TABLE_BODY ||
- $this->mode === self::IN_ROW || $this->mode === self::IN_CELL
- ) {
- $this->mode = self::IN_SELECT_IN_TABLE;
- } else {
- $this->mode = self::IN_SELECT;
- }
- break;
-
- case 'option': case 'optgroup':
- if ($this->elementInScope('option')) {
- $this->emitToken(array(
- 'name' => 'option',
- 'type' => HTML5_Tokenizer::ENDTAG,
- ));
- }
- $this->reconstructActiveFormattingElements();
- $this->insertElement($token);
- break;
-
- case 'rp': case 'rt':
- /* If the stack of open elements has a ruby element in scope, then generate
- * implied end tags. If the current node is not then a ruby element, this is
- * a parse error; pop all the nodes from the current node up to the node
- * immediately before the bottommost ruby element on the stack of open elements.
- */
- if ($this->elementInScope('ruby')) {
- $this->generateImpliedEndTags();
- }
- $peek = false;
- do {
- /*if ($peek) {
- // parse error
- }*/
- $peek = array_pop($this->stack);
- } while ($peek->tagName !== 'ruby');
- $this->stack[] = $peek; // we popped one too many
- $this->insertElement($token);
- break;
-
- // spec diversion
-
- case 'math':
- $this->reconstructActiveFormattingElements();
- $token = $this->adjustMathMLAttributes($token);
- $token = $this->adjustForeignAttributes($token);
- $this->insertForeignElement($token, self::NS_MATHML);
- if (isset($token['self-closing'])) {
- // XERROR: acknowledge the token's self-closing flag
- array_pop($this->stack);
- }
- if ($this->mode !== self::IN_FOREIGN_CONTENT) {
- $this->secondary_mode = $this->mode;
- $this->mode = self::IN_FOREIGN_CONTENT;
- }
- break;
-
- case 'svg':
- $this->reconstructActiveFormattingElements();
- $token = $this->adjustSVGAttributes($token);
- $token = $this->adjustForeignAttributes($token);
- $this->insertForeignElement($token, self::NS_SVG);
- if (isset($token['self-closing'])) {
- // XERROR: acknowledge the token's self-closing flag
- array_pop($this->stack);
- }
- if ($this->mode !== self::IN_FOREIGN_CONTENT) {
- $this->secondary_mode = $this->mode;
- $this->mode = self::IN_FOREIGN_CONTENT;
- }
- break;
-
- case 'caption': case 'col': case 'colgroup': case 'frame': case 'head':
- case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead': case 'tr':
- // parse error
- break;
-
- /* A start tag token not covered by the previous entries */
- default:
- /* Reconstruct the active formatting elements, if any. */
- $this->reconstructActiveFormattingElements();
-
- $this->insertElement($token);
- /* This element will be a phrasing element. */
- break;
- }
- break;
-
- case HTML5_Tokenizer::ENDTAG:
- switch ($token['name']) {
- /* An end tag with the tag name "body" */
- case 'body':
- /* If the stack of open elements does not have a body
- * element in scope, this is a parse error; ignore the
- * token. */
- if (!$this->elementInScope('body')) {
- $this->ignored = true;
-
- /* Otherwise, if there is a node in the stack of open
- * elements that is not either a dc element, a dd element,
- * a ds element, a dt element, an li element, an optgroup
- * element, an option element, a p element, an rp element,
- * an rt element, a tbody element, a td element, a tfoot
- * element, a th element, a thead element, a tr element,
- * the body element, or the html element, then this is a
- * parse error.
- */
- } else {
- // XERROR: implement this check for parse error
- }
-
- /* Change the insertion mode to "after body". */
- $this->mode = self::AFTER_BODY;
- break;
-
- /* An end tag with the tag name "html" */
- case 'html':
- /* Act as if an end tag with tag name "body" had been seen,
- then, if that token wasn't ignored, reprocess the current
- token. */
- $this->emitToken(array(
- 'name' => 'body',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- if (!$this->ignored) {
- $this->emitToken($token);
- }
- break;
-
- case 'address': case 'article': case 'aside': case 'blockquote':
- case 'center': case 'datagrid': case 'details': case 'dir':
- case 'div': case 'dl': case 'fieldset': case 'footer':
- case 'header': case 'hgroup': case 'listing': case 'menu':
- case 'nav': case 'ol': case 'pre': case 'section': case 'ul':
- /* If the stack of open elements has an element in scope
- with the same tag name as that of the token, then generate
- implied end tags. */
- if ($this->elementInScope($token['name'])) {
- $this->generateImpliedEndTags();
-
- /* Now, if the current node is not an element with
- the same tag name as that of the token, then this
- is a parse error. */
- // XERROR: implement parse error logic
-
- /* If the stack of open elements has an element in
- scope with the same tag name as that of the token,
- then pop elements from this stack until an element
- with that tag name has been popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== $token['name']);
- } else {
- // parse error
- }
- break;
-
- /* An end tag whose tag name is "form" */
- case 'form':
- /* Let node be the element that the form element pointer is set to. */
- $node = $this->form_pointer;
- /* Set the form element pointer to null. */
- $this->form_pointer = null;
- /* If node is null or the stack of open elements does not
- * have node in scope, then this is a parse error; ignore the token. */
- if ($node === null || !in_array($node, $this->stack)) {
- // parse error
- $this->ignored = true;
- } else {
- /* 1. Generate implied end tags. */
- $this->generateImpliedEndTags();
- /* 2. If the current node is not node, then this is a parse error. */
- if (end($this->stack) !== $node) {
- // parse error
- }
- /* 3. Remove node from the stack of open elements. */
- array_splice($this->stack, array_search($node, $this->stack, true), 1);
- }
-
- break;
-
- /* An end tag whose tag name is "p" */
- case 'p':
- /* If the stack of open elements has a p element in scope,
- then generate implied end tags, except for p elements. */
- if ($this->elementInScope('p')) {
- /* Generate implied end tags, except for elements with
- * the same tag name as the token. */
- $this->generateImpliedEndTags(array('p'));
-
- /* If the current node is not a p element, then this is
- a parse error. */
- // XERROR: implement
-
- /* Pop elements from the stack of open elements until
- * an element with the same tag name as the token has
- * been popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== 'p');
-
- } else {
- // parse error
- $this->emitToken(array(
- 'name' => 'p',
- 'type' => HTML5_Tokenizer::STARTTAG,
- ));
- $this->emitToken($token);
- }
- break;
-
- /* An end tag whose tag name is "li" */
- case 'li':
- /* If the stack of open elements does not have an element
- * in list item scope with the same tag name as that of the
- * token, then this is a parse error; ignore the token. */
- if ($this->elementInScope($token['name'], self::SCOPE_LISTITEM)) {
- /* Generate implied end tags, except for elements with the
- * same tag name as the token. */
- $this->generateImpliedEndTags(array($token['name']));
- /* If the current node is not an element with the same tag
- * name as that of the token, then this is a parse error. */
- // XERROR: parse error
- /* Pop elements from the stack of open elements until an
- * element with the same tag name as the token has been
- * popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== $token['name']);
- }
- /*else {
- // XERROR: parse error
- }*/
- break;
-
- /* An end tag whose tag name is "dc", "dd", "ds", "dt" */
- case 'dc': case 'dd': case 'ds': case 'dt':
- if ($this->elementInScope($token['name'])) {
- $this->generateImpliedEndTags(array($token['name']));
-
- /* If the current node is not an element with the same
- tag name as the token, then this is a parse error. */
- // XERROR: implement parse error
-
- /* Pop elements from the stack of open elements until
- * an element with the same tag name as the token has
- * been popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== $token['name']);
- }
- /*else {
- // XERROR: parse error
- }*/
- break;
-
- /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4",
- "h5", "h6" */
- case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
- $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');
-
- /* If the stack of open elements has in scope an element whose
- tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
- generate implied end tags. */
- if ($this->elementInScope($elements)) {
- $this->generateImpliedEndTags();
-
- /* Now, if the current node is not an element with the same
- tag name as that of the token, then this is a parse error. */
- // XERROR: implement parse error
-
- /* If the stack of open elements has in scope an element
- whose tag name is one of "h1", "h2", "h3", "h4", "h5", or
- "h6", then pop elements from the stack until an element
- with one of those tag names has been popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while (!in_array($node->tagName, $elements));
- }
- /*else {
- // parse error
- }*/
- break;
-
- /* An end tag whose tag name is one of: "a", "b", "big", "em",
- "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
- case 'a': case 'b': case 'big': case 'code': case 'em': case 'font':
- case 'i': case 'nobr': case 's': case 'small': case 'strike':
- case 'strong': case 'tt': case 'u':
- // XERROR: generally speaking this needs parse error logic
- /* 1. Let the formatting element be the last element in
- the list of active formatting elements that:
- * is between the end of the list and the last scope
- marker in the list, if any, or the start of the list
- otherwise, and
- * has the same tag name as the token.
- */
- while (true) {
- for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) {
- if ($this->a_formatting[$a] === self::MARKER) {
- break;
- } elseif ($this->a_formatting[$a]->tagName === $token['name']) {
- $formatting_element = $this->a_formatting[$a];
- $in_stack = in_array($formatting_element, $this->stack, true);
- $fe_af_pos = $a;
- break;
- }
- }
-
- /* If there is no such node, or, if that node is
- also in the stack of open elements but the element
- is not in scope, then this is a parse error. Abort
- these steps. The token is ignored. */
- if (
- !isset($formatting_element) || (
- $in_stack &&
- !$this->elementInScope($token['name'])
- )
- ) {
- $this->ignored = true;
- break;
-
- /* Otherwise, if there is such a node, but that node
- is not in the stack of open elements, then this is a
- parse error; remove the element from the list, and
- abort these steps. */
- } elseif (isset($formatting_element) && !$in_stack) {
- unset($this->a_formatting[$fe_af_pos]);
- $this->a_formatting = array_merge($this->a_formatting);
- break;
- }
-
- /* Otherwise, there is a formatting element and that
- * element is in the stack and is in scope. If the
- * element is not the current node, this is a parse
- * error. In any case, proceed with the algorithm as
- * written in the following steps. */
- // XERROR: implement me
-
- /* 2. Let the furthest block be the topmost node in the
- stack of open elements that is lower in the stack
- than the formatting element, and is not an element in
- the phrasing or formatting categories. There might
- not be one. */
- $fe_s_pos = array_search($formatting_element, $this->stack, true);
- $length = count($this->stack);
-
- for ($s = $fe_s_pos + 1; $s < $length; $s++) {
- $category = $this->getElementCategory($this->stack[$s]);
-
- if ($category !== self::PHRASING && $category !== self::FORMATTING) {
- $furthest_block = $this->stack[$s];
- break;
- }
- }
-
- /* 3. If there is no furthest block, then the UA must
- skip the subsequent steps and instead just pop all
- the nodes from the bottom of the stack of open
- elements, from the current node up to the formatting
- element, and remove the formatting element from the
- list of active formatting elements. */
- if (!isset($furthest_block)) {
- for ($n = $length - 1; $n >= $fe_s_pos; $n--) {
- array_pop($this->stack);
- }
-
- unset($this->a_formatting[$fe_af_pos]);
- $this->a_formatting = array_merge($this->a_formatting);
- break;
- }
-
- /* 4. Let the common ancestor be the element
- immediately above the formatting element in the stack
- of open elements. */
- $common_ancestor = $this->stack[$fe_s_pos - 1];
-
- /* 5. Let a bookmark note the position of the
- formatting element in the list of active formatting
- elements relative to the elements on either side
- of it in the list. */
- $bookmark = $fe_af_pos;
-
- /* 6. Let node and last node be the furthest block.
- Follow these steps: */
- $node = $furthest_block;
- $last_node = $furthest_block;
-
- while (true) {
- for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
- /* 6.1 Let node be the element immediately
- prior to node in the stack of open elements. */
- $node = $this->stack[$n];
-
- /* 6.2 If node is not in the list of active
- formatting elements, then remove node from
- the stack of open elements and then go back
- to step 1. */
- if (!in_array($node, $this->a_formatting, true)) {
- array_splice($this->stack, $n, 1);
- } else {
- break;
- }
- }
-
- /* 6.3 Otherwise, if node is the formatting
- element, then go to the next step in the overall
- algorithm. */
- if ($node === $formatting_element) {
- break;
-
- /* 6.4 Otherwise, if last node is the furthest
- block, then move the aforementioned bookmark to
- be immediately after the node in the list of
- active formatting elements. */
- } elseif ($last_node === $furthest_block) {
- $bookmark = array_search($node, $this->a_formatting, true) + 1;
- }
-
- /* 6.5 Create an element for the token for which
- * the element node was created, replace the entry
- * for node in the list of active formatting
- * elements with an entry for the new element,
- * replace the entry for node in the stack of open
- * elements with an entry for the new element, and
- * let node be the new element. */
- // we don't know what the token is anymore
- // XDOM
- $clone = $node->cloneNode();
- $a_pos = array_search($node, $this->a_formatting, true);
- $s_pos = array_search($node, $this->stack, true);
- $this->a_formatting[$a_pos] = $clone;
- $this->stack[$s_pos] = $clone;
- $node = $clone;
-
- /* 6.6 Insert last node into node, first removing
- it from its previous parent node if any. */
- // XDOM
- if ($last_node->parentNode !== null) {
- $last_node->parentNode->removeChild($last_node);
- }
-
- // XDOM
- $node->appendChild($last_node);
-
- /* 6.7 Let last node be node. */
- $last_node = $node;
-
- /* 6.8 Return to step 1 of this inner set of steps. */
- }
-
- /* 7. If the common ancestor node is a table, tbody,
- * tfoot, thead, or tr element, then, foster parent
- * whatever last node ended up being in the previous
- * step, first removing it from its previous parent
- * node if any. */
- // XDOM
- if ($last_node->parentNode) { // common step
- $last_node->parentNode->removeChild($last_node);
- }
- if (in_array($common_ancestor->tagName, array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
- $this->fosterParent($last_node);
- /* Otherwise, append whatever last node ended up being
- * in the previous step to the common ancestor node,
- * first removing it from its previous parent node if
- * any. */
- } else {
- // XDOM
- $common_ancestor->appendChild($last_node);
- }
-
- /* 8. Create an element for the token for which the
- * formatting element was created. */
- // XDOM
- $clone = $formatting_element->cloneNode();
-
- /* 9. Take all of the child nodes of the furthest
- block and append them to the element created in the
- last step. */
- // XDOM
- while ($furthest_block->hasChildNodes()) {
- $child = $furthest_block->firstChild;
- $furthest_block->removeChild($child);
- $clone->appendChild($child);
- }
-
- /* 10. Append that clone to the furthest block. */
- // XDOM
- $furthest_block->appendChild($clone);
-
- /* 11. Remove the formatting element from the list
- of active formatting elements, and insert the new element
- into the list of active formatting elements at the
- position of the aforementioned bookmark. */
- $fe_af_pos = array_search($formatting_element, $this->a_formatting, true);
- array_splice($this->a_formatting, $fe_af_pos, 1);
-
- $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
- $af_part2 = array_slice($this->a_formatting, $bookmark);
- $this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
-
- /* 12. Remove the formatting element from the stack
- of open elements, and insert the new element into the stack
- of open elements immediately below the position of the
- furthest block in that stack. */
- $fe_s_pos = array_search($formatting_element, $this->stack, true);
- array_splice($this->stack, $fe_s_pos, 1);
-
- $fb_s_pos = array_search($furthest_block, $this->stack, true);
- $s_part1 = array_slice($this->stack, 0, $fb_s_pos + 1);
- $s_part2 = array_slice($this->stack, $fb_s_pos + 1);
- $this->stack = array_merge($s_part1, array($clone), $s_part2);
-
- /* 13. Jump back to step 1 in this series of steps. */
- unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
- }
- break;
-
- case 'applet': case 'button': case 'marquee': case 'object':
- /* If the stack of open elements has an element in scope whose
- tag name matches the tag name of the token, then generate implied
- tags. */
- if ($this->elementInScope($token['name'])) {
- $this->generateImpliedEndTags();
-
- /* Now, if the current node is not an element with the same
- tag name as the token, then this is a parse error. */
- // XERROR: implement logic
-
- /* Pop elements from the stack of open elements until
- * an element with the same tag name as the token has
- * been popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== $token['name']);
-
- /* Clear the list of active formatting elements up to the
- * last marker. */
- $keys = array_keys($this->a_formatting, self::MARKER, true);
- $marker = end($keys);
-
- for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
- array_pop($this->a_formatting);
- }
- }
- /*else {
- // parse error
- }*/
- break;
-
- case 'br':
- // Parse error
- $this->emitToken(array(
- 'name' => 'br',
- 'type' => HTML5_Tokenizer::STARTTAG,
- ));
- break;
-
- /* An end tag token not covered by the previous entries */
- default:
- for ($n = count($this->stack) - 1; $n >= 0; $n--) {
- /* Initialise node to be the current node (the bottommost
- node of the stack). */
- $node = $this->stack[$n];
-
- /* If node has the same tag name as the end tag token,
- then: */
- if ($token['name'] === $node->tagName) {
- /* Generate implied end tags. */
- $this->generateImpliedEndTags();
-
- /* If the tag name of the end tag token does not
- match the tag name of the current node, this is a
- parse error. */
- // XERROR: implement this
-
- /* Pop all the nodes from the current node up to
- node, including node, then stop these steps. */
- // XSKETCHY
- do {
- $pop = array_pop($this->stack);
- } while ($pop !== $node);
- break;
- } else {
- $category = $this->getElementCategory($node);
-
- if ($category !== self::FORMATTING && $category !== self::PHRASING) {
- /* Otherwise, if node is in neither the formatting
- category nor the phrasing category, then this is a
- parse error. Stop this algorithm. The end tag token
- is ignored. */
- $this->ignored = true;
- break;
- // parse error
- }
- }
- /* Set node to the previous entry in the stack of open elements. Loop. */
- }
- break;
- }
- break;
- }
- break;
-
- case self::IN_CDATA_RCDATA:
- if (
- $token['type'] === HTML5_Tokenizer::CHARACTER ||
- $token['type'] === HTML5_Tokenizer::SPACECHARACTER
- ) {
- $this->insertText($token['data']);
- } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
- // parse error
- /* If the current node is a script element, mark the script
- * element as "already executed". */
- // probably not necessary
- array_pop($this->stack);
- $this->mode = $this->original_mode;
- $this->emitToken($token);
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'script') {
- array_pop($this->stack);
- $this->mode = $this->original_mode;
- // we're ignoring all of the execution stuff
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) {
- array_pop($this->stack);
- $this->mode = $this->original_mode;
- }
- break;
-
- case self::IN_TABLE:
- $clear = array('html', 'table');
-
- /* A character token */
- if ($token['type'] === HTML5_Tokenizer::CHARACTER ||
- $token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Let the pending table character tokens
- * be an empty list of tokens. */
- $this->pendingTableCharacters = "";
- $this->pendingTableCharactersDirty = false;
- /* Let the original insertion mode be the current
- * insertion mode. */
- $this->original_mode = $this->mode;
- /* Switch the insertion mode to
- * "in table text" and
- * reprocess the token. */
- $this->mode = self::IN_TABLE_TEXT;
- $this->emitToken($token);
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the current node with the data
- attribute set to the data given in the comment token. */
- $this->insertComment($token['data']);
-
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // parse error
-
- /* A start tag whose tag name is "caption" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'caption') {
- /* Clear the stack back to a table context. */
- $this->clearStackToTableContext($clear);
-
- /* Insert a marker at the end of the list of active
- formatting elements. */
- $this->a_formatting[] = self::MARKER;
-
- /* Insert an HTML element for the token, then switch the
- insertion mode to "in caption". */
- $this->insertElement($token);
- $this->mode = self::IN_CAPTION;
-
- /* A start tag whose tag name is "colgroup" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'colgroup') {
- /* Clear the stack back to a table context. */
- $this->clearStackToTableContext($clear);
-
- /* Insert an HTML element for the token, then switch the
- insertion mode to "in column group". */
- $this->insertElement($token);
- $this->mode = self::IN_COLUMN_GROUP;
-
- /* A start tag whose tag name is "col" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'col') {
- $this->emitToken(array(
- 'name' => 'colgroup',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => array()
- ));
-
- $this->emitToken($token);
-
- /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
- array('tbody', 'tfoot', 'thead'))) {
- /* Clear the stack back to a table context. */
- $this->clearStackToTableContext($clear);
-
- /* Insert an HTML element for the token, then switch the insertion
- mode to "in table body". */
- $this->insertElement($token);
- $this->mode = self::IN_TABLE_BODY;
-
- /* A start tag whose tag name is one of: "td", "th", "tr" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- in_array($token['name'], array('td', 'th', 'tr'))) {
- /* Act as if a start tag token with the tag name "tbody" had been
- seen, then reprocess the current token. */
- $this->emitToken(array(
- 'name' => 'tbody',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => array()
- ));
-
- $this->emitToken($token);
-
- /* A start tag whose tag name is "table" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'table') {
- /* Parse error. Act as if an end tag token with the tag name "table"
- had been seen, then, if that token wasn't ignored, reprocess the
- current token. */
- $this->emitToken(array(
- 'name' => 'table',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- if (!$this->ignored) {
- $this->emitToken($token);
- }
-
- /* An end tag whose tag name is "table" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'table') {
- /* If the stack of open elements does not have an element in table
- scope with the same tag name as the token, this is a parse error.
- Ignore the token. (fragment case) */
- if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
- $this->ignored = true;
- } else {
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== 'table');
-
- /* Reset the insertion mode appropriately. */
- $this->resetInsertionMode();
- }
-
- /* An end tag whose tag name is one of: "body", "caption", "col",
- "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
- array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td',
- 'tfoot', 'th', 'thead', 'tr'))) {
- // Parse error. Ignore the token.
-
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- ($token['name'] === 'style' || $token['name'] === 'script')) {
- $this->processWithRulesFor($token, self::IN_HEAD);
-
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'input' &&
- // assignment is intentional
- /* If the token does not have an attribute with the name "type", or
- * if it does, but that attribute's value is not an ASCII
- * case-insensitive match for the string "hidden", then: act as
- * described in the "anything else" entry below. */
- ($type = $this->getAttr($token, 'type')) && strtolower($type) === 'hidden') {
- // I.e., if its an input with the type attribute == 'hidden'
- /* Otherwise */
- // parse error
- $this->insertElement($token);
- array_pop($this->stack);
- } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
- /* If the current node is not the root html element, then this is a parse error. */
- if (end($this->stack)->tagName !== 'html') {
- // Note: It can only be the current node in the fragment case.
- // parse error
- }
- /* Stop parsing. */
- /* Anything else */
- } else {
- /* Parse error. Process the token as if the insertion mode was "in
- body", with the following exception: */
-
- $old = $this->foster_parent;
- $this->foster_parent = true;
- $this->processWithRulesFor($token, self::IN_BODY);
- $this->foster_parent = $old;
- }
- break;
-
- case self::IN_TABLE_TEXT:
- /* A character token */
- if ($token['type'] === HTML5_Tokenizer::CHARACTER) {
- /* Append the character token to the pending table
- * character tokens list. */
- $this->pendingTableCharacters .= $token['data'];
- $this->pendingTableCharactersDirty = true;
- } elseif ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- $this->pendingTableCharacters .= $token['data'];
- /* Anything else */
- } else {
- if ($this->pendingTableCharacters !== '' && is_string($this->pendingTableCharacters)) {
- /* If any of the tokens in the pending table character tokens list
- * are character tokens that are not one of U+0009 CHARACTER
- * TABULATION, U+000A LINE FEED (LF), U+000C FORM FEED (FF), or
- * U+0020 SPACE, then reprocess those character tokens using the
- * rules given in the "anything else" entry in the in table"
- * insertion mode.*/
- if ($this->pendingTableCharactersDirty) {
- /* Parse error. Process the token using the rules for the
- * "in body" insertion mode, except that if the current
- * node is a table, tbody, tfoot, thead, or tr element,
- * then, whenever a node would be inserted into the current
- * node, it must instead be foster parented. */
- // XERROR
- $old = $this->foster_parent;
- $this->foster_parent = true;
- $text_token = array(
- 'type' => HTML5_Tokenizer::CHARACTER,
- 'data' => $this->pendingTableCharacters,
- );
- $this->processWithRulesFor($text_token, self::IN_BODY);
- $this->foster_parent = $old;
-
- /* Otherwise, insert the characters given by the pending table
- * character tokens list into the current node. */
- } else {
- $this->insertText($this->pendingTableCharacters);
- }
- $this->pendingTableCharacters = null;
- $this->pendingTableCharactersNull = null;
- }
-
- /* Switch the insertion mode to the original insertion mode and
- * reprocess the token.
- */
- $this->mode = $this->original_mode;
- $this->emitToken($token);
- }
- break;
-
- case self::IN_CAPTION:
- /* An end tag whose tag name is "caption" */
- if ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'caption') {
- /* If the stack of open elements does not have an element in table
- scope with the same tag name as the token, this is a parse error.
- Ignore the token. (fragment case) */
- if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
- $this->ignored = true;
- // Ignore
-
- /* Otherwise: */
- } else {
- /* Generate implied end tags. */
- $this->generateImpliedEndTags();
-
- /* Now, if the current node is not a caption element, then this
- is a parse error. */
- // XERROR: implement
-
- /* Pop elements from this stack until a caption element has
- been popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== 'caption');
-
- /* Clear the list of active formatting elements up to the last
- marker. */
- $this->clearTheActiveFormattingElementsUpToTheLastMarker();
-
- /* Switch the insertion mode to "in table". */
- $this->mode = self::IN_TABLE;
- }
-
- /* A start tag whose tag name is one of: "caption", "col", "colgroup",
- "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag
- name is "table" */
- } elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
- 'thead', 'tr'))) || ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'table')) {
- /* Parse error. Act as if an end tag with the tag name "caption"
- had been seen, then, if that token wasn't ignored, reprocess the
- current token. */
- $this->emitToken(array(
- 'name' => 'caption',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- if (!$this->ignored) {
- $this->emitToken($token);
- }
-
- /* An end tag whose tag name is one of: "body", "col", "colgroup",
- "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
- array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th',
- 'thead', 'tr'))) {
- // Parse error. Ignore the token.
- $this->ignored = true;
- } else {
- /* Process the token as if the insertion mode was "in body". */
- $this->processWithRulesFor($token, self::IN_BODY);
- }
- break;
-
- case self::IN_COLUMN_GROUP:
- /* A character token that is one of one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- or U+0020 SPACE */
- if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Append the character to the current node. */
- $this->insertText($token['data']);
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the current node with the data
- attribute set to the data given in the comment token. */
- $this->insertComment($token['data']);
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // parse error
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* A start tag whose tag name is "col" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'col') {
- /* Insert a col element for the token. Immediately pop the current
- node off the stack of open elements. */
- $this->insertElement($token);
- array_pop($this->stack);
- // XERROR: Acknowledge the token's self-closing flag, if it is set.
-
- /* An end tag whose tag name is "colgroup" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'colgroup') {
- /* If the current node is the root html element, then this is a
- parse error, ignore the token. (fragment case) */
- if (end($this->stack)->tagName === 'html') {
- $this->ignored = true;
-
- /* Otherwise, pop the current node (which will be a colgroup
- element) from the stack of open elements. Switch the insertion
- mode to "in table". */
- } else {
- array_pop($this->stack);
- $this->mode = self::IN_TABLE;
- }
-
- /* An end tag whose tag name is "col" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'col') {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
-
- /* An end-of-file token */
- /* If the current node is the root html element */
- } elseif ($token['type'] === HTML5_Tokenizer::EOF && end($this->stack)->tagName === 'html') {
- /* Stop parsing */
-
- /* Anything else */
- } else {
- /* Act as if an end tag with the tag name "colgroup" had been seen,
- and then, if that token wasn't ignored, reprocess the current token. */
- $this->emitToken(array(
- 'name' => 'colgroup',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- if (!$this->ignored) {
- $this->emitToken($token);
- }
- }
- break;
-
- case self::IN_TABLE_BODY:
- $clear = array('tbody', 'tfoot', 'thead', 'html');
-
- /* A start tag whose tag name is "tr" */
- if ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'tr') {
- /* Clear the stack back to a table body context. */
- $this->clearStackToTableContext($clear);
-
- /* Insert a tr element for the token, then switch the insertion
- mode to "in row". */
- $this->insertElement($token);
- $this->mode = self::IN_ROW;
-
- /* A start tag whose tag name is one of: "th", "td" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- ($token['name'] === 'th' || $token['name'] === 'td')) {
- /* Parse error. Act as if a start tag with the tag name "tr" had
- been seen, then reprocess the current token. */
- $this->emitToken(array(
- 'name' => 'tr',
- 'type' => HTML5_Tokenizer::STARTTAG,
- 'attr' => array()
- ));
-
- $this->emitToken($token);
-
- /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
- /* If the stack of open elements does not have an element in table
- scope with the same tag name as the token, this is a parse error.
- Ignore the token. */
- if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
- // Parse error
- $this->ignored = true;
-
- /* Otherwise: */
- } else {
- /* Clear the stack back to a table body context. */
- $this->clearStackToTableContext($clear);
-
- /* Pop the current node from the stack of open elements. Switch
- the insertion mode to "in table". */
- array_pop($this->stack);
- $this->mode = self::IN_TABLE;
- }
-
- /* A start tag whose tag name is one of: "caption", "col", "colgroup",
- "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */
- } elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead'))) ||
- ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) {
- /* If the stack of open elements does not have a tbody, thead, or
- tfoot element in table scope, this is a parse error. Ignore the
- token. (fragment case) */
- if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), self::SCOPE_TABLE)) {
- // parse error
- $this->ignored = true;
-
- /* Otherwise: */
- } else {
- /* Clear the stack back to a table body context. */
- $this->clearStackToTableContext($clear);
-
- /* Act as if an end tag with the same tag name as the current
- node ("tbody", "tfoot", or "thead") had been seen, then
- reprocess the current token. */
- $this->emitToken(array(
- 'name' => end($this->stack)->tagName,
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- $this->emitToken($token);
- }
-
- /* An end tag whose tag name is one of: "body", "caption", "col",
- "colgroup", "html", "td", "th", "tr" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
- array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
-
- /* Anything else */
- } else {
- /* Process the token as if the insertion mode was "in table". */
- $this->processWithRulesFor($token, self::IN_TABLE);
- }
- break;
-
- case self::IN_ROW:
- $clear = array('tr', 'html');
-
- /* A start tag whose tag name is one of: "th", "td" */
- if ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- ($token['name'] === 'th' || $token['name'] === 'td')) {
- /* Clear the stack back to a table row context. */
- $this->clearStackToTableContext($clear);
-
- /* Insert an HTML element for the token, then switch the insertion
- mode to "in cell". */
- $this->insertElement($token);
- $this->mode = self::IN_CELL;
-
- /* Insert a marker at the end of the list of active formatting
- elements. */
- $this->a_formatting[] = self::MARKER;
-
- /* An end tag whose tag name is "tr" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'tr') {
- /* If the stack of open elements does not have an element in table
- scope with the same tag name as the token, this is a parse error.
- Ignore the token. (fragment case) */
- if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
- // Ignore.
- $this->ignored = true;
- } else {
- /* Clear the stack back to a table row context. */
- $this->clearStackToTableContext($clear);
-
- /* Pop the current node (which will be a tr element) from the
- stack of open elements. Switch the insertion mode to "in table
- body". */
- array_pop($this->stack);
- $this->mode = self::IN_TABLE_BODY;
- }
-
- /* A start tag whose tag name is one of: "caption", "col", "colgroup",
- "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */
- } elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) ||
- ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) {
- /* Act as if an end tag with the tag name "tr" had been seen, then,
- if that token wasn't ignored, reprocess the current token. */
- $this->emitToken(array(
- 'name' => 'tr',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- if (!$this->ignored) {
- $this->emitToken($token);
- }
-
- /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
- /* If the stack of open elements does not have an element in table
- scope with the same tag name as the token, this is a parse error.
- Ignore the token. */
- if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
- $this->ignored = true;
-
- /* Otherwise: */
- } else {
- /* Otherwise, act as if an end tag with the tag name "tr" had
- been seen, then reprocess the current token. */
- $this->emitToken(array(
- 'name' => 'tr',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- $this->emitToken($token);
- }
-
- /* An end tag whose tag name is one of: "body", "caption", "col",
- "colgroup", "html", "td", "th" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
- array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th'))) {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
-
- /* Anything else */
- } else {
- /* Process the token as if the insertion mode was "in table". */
- $this->processWithRulesFor($token, self::IN_TABLE);
- }
- break;
-
- case self::IN_CELL:
- /* An end tag whose tag name is one of: "td", "th" */
- if ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- ($token['name'] === 'td' || $token['name'] === 'th')) {
- /* If the stack of open elements does not have an element in table
- scope with the same tag name as that of the token, then this is a
- parse error and the token must be ignored. */
- if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
- $this->ignored = true;
-
- /* Otherwise: */
- } else {
- /* Generate implied end tags, except for elements with the same
- tag name as the token. */
- $this->generateImpliedEndTags(array($token['name']));
-
- /* Now, if the current node is not an element with the same tag
- name as the token, then this is a parse error. */
- // XERROR: Implement parse error code
-
- /* Pop elements from this stack until an element with the same
- tag name as the token has been popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== $token['name']);
-
- /* Clear the list of active formatting elements up to the last
- marker. */
- $this->clearTheActiveFormattingElementsUpToTheLastMarker();
-
- /* Switch the insertion mode to "in row". (The current node
- will be a tr element at this point.) */
- $this->mode = self::IN_ROW;
- }
-
- /* A start tag whose tag name is one of: "caption", "col", "colgroup",
- "tbody", "td", "tfoot", "th", "thead", "tr" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
- array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
- 'thead', 'tr'))) {
- /* If the stack of open elements does not have a td or th element
- in table scope, then this is a parse error; ignore the token.
- (fragment case) */
- if (!$this->elementInScope(array('td', 'th'), self::SCOPE_TABLE)) {
- // parse error
- $this->ignored = true;
-
- /* Otherwise, close the cell (see below) and reprocess the current
- token. */
- } else {
- $this->closeCell();
- $this->emitToken($token);
- }
-
- /* An end tag whose tag name is one of: "body", "caption", "col",
- "colgroup", "html" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
- array('body', 'caption', 'col', 'colgroup', 'html'))) {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
-
- /* An end tag whose tag name is one of: "table", "tbody", "tfoot",
- "thead", "tr" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
- array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
- /* If the stack of open elements does not have a td or th element
- in table scope, then this is a parse error; ignore the token.
- (innerHTML case) */
- if (!$this->elementInScope(array('td', 'th'), self::SCOPE_TABLE)) {
- // Parse error
- $this->ignored = true;
-
- /* Otherwise, close the cell (see below) and reprocess the current
- token. */
- } else {
- $this->closeCell();
- $this->emitToken($token);
- }
-
- /* Anything else */
- } else {
- /* Process the token as if the insertion mode was "in body". */
- $this->processWithRulesFor($token, self::IN_BODY);
- }
- break;
-
- case self::IN_SELECT:
- /* Handle the token as follows: */
-
- /* A character token */
- if (
- $token['type'] === HTML5_Tokenizer::CHARACTER ||
- $token['type'] === HTML5_Tokenizer::SPACECHARACTER
- ) {
- /* Append the token's character to the current node. */
- $this->insertText($token['data']);
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the current node with the data
- attribute set to the data given in the comment token. */
- $this->insertComment($token['data']);
-
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // parse error
-
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* A start tag token whose tag name is "option" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'option') {
- /* If the current node is an option element, act as if an end tag
- with the tag name "option" had been seen. */
- if (end($this->stack)->tagName === 'option') {
- $this->emitToken(array(
- 'name' => 'option',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* A start tag token whose tag name is "optgroup" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'optgroup') {
- /* If the current node is an option element, act as if an end tag
- with the tag name "option" had been seen. */
- if (end($this->stack)->tagName === 'option') {
- $this->emitToken(array(
- 'name' => 'option',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* If the current node is an optgroup element, act as if an end tag
- with the tag name "optgroup" had been seen. */
- if (end($this->stack)->tagName === 'optgroup') {
- $this->emitToken(array(
- 'name' => 'optgroup',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* An end tag token whose tag name is "optgroup" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'optgroup') {
- /* First, if the current node is an option element, and the node
- immediately before it in the stack of open elements is an optgroup
- element, then act as if an end tag with the tag name "option" had
- been seen. */
- $elements_in_stack = count($this->stack);
-
- if ($this->stack[$elements_in_stack - 1]->tagName === 'option' &&
- $this->stack[$elements_in_stack - 2]->tagName === 'optgroup') {
- $this->emitToken(array(
- 'name' => 'option',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- }
-
- /* If the current node is an optgroup element, then pop that node
- from the stack of open elements. Otherwise, this is a parse error,
- ignore the token. */
- if (end($this->stack)->tagName === 'optgroup') {
- array_pop($this->stack);
- } else {
- // parse error
- $this->ignored = true;
- }
-
- /* An end tag token whose tag name is "option" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'option') {
- /* If the current node is an option element, then pop that node
- from the stack of open elements. Otherwise, this is a parse error,
- ignore the token. */
- if (end($this->stack)->tagName === 'option') {
- array_pop($this->stack);
- } else {
- // parse error
- $this->ignored = true;
- }
-
- /* An end tag whose tag name is "select" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'select') {
- /* If the stack of open elements does not have an element in table
- scope with the same tag name as the token, this is a parse error.
- Ignore the token. (fragment case) */
- if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
- $this->ignored = true;
- // parse error
-
- /* Otherwise: */
- } else {
- /* Pop elements from the stack of open elements until a select
- element has been popped from the stack. */
- do {
- $node = array_pop($this->stack);
- } while ($node->tagName !== 'select');
-
- /* Reset the insertion mode appropriately. */
- $this->resetInsertionMode();
- }
-
- /* A start tag whose tag name is "select" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'select') {
- /* Parse error. Act as if the token had been an end tag with the
- tag name "select" instead. */
- $this->emitToken(array(
- 'name' => 'select',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- ($token['name'] === 'input' || $token['name'] === 'keygen' || $token['name'] === 'textarea')) {
- // parse error
- $this->emitToken(array(
- 'name' => 'select',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
- $this->emitToken($token);
-
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') {
- $this->processWithRulesFor($token, self::IN_HEAD);
-
- } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
- // XERROR: If the current node is not the root html element, then this is a parse error.
- /* Stop parsing */
-
- /* Anything else */
- } else {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
- }
- break;
-
- case self::IN_SELECT_IN_TABLE:
-
- if ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- in_array($token['name'], array('caption', 'table', 'tbody',
- 'tfoot', 'thead', 'tr', 'td', 'th'))) {
- // parse error
- $this->emitToken(array(
- 'name' => 'select',
- 'type' => HTML5_Tokenizer::ENDTAG,
- ));
- $this->emitToken($token);
-
- /* An end tag whose tag name is one of: "caption", "table", "tbody",
- "tfoot", "thead", "tr", "td", "th" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- in_array($token['name'], array('caption', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'td', 'th'))) {
- /* Parse error. */
- // parse error
-
- /* If the stack of open elements has an element in table scope with
- the same tag name as that of the token, then act as if an end tag
- with the tag name "select" had been seen, and reprocess the token.
- Otherwise, ignore the token. */
- if ($this->elementInScope($token['name'], self::SCOPE_TABLE)) {
- $this->emitToken(array(
- 'name' => 'select',
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- $this->emitToken($token);
- } else {
- $this->ignored = true;
- }
- } else {
- $this->processWithRulesFor($token, self::IN_SELECT);
- }
- break;
-
- case self::IN_FOREIGN_CONTENT:
- if ($token['type'] === HTML5_Tokenizer::CHARACTER ||
- $token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- $this->insertText($token['data']);
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- $this->insertComment($token['data']);
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // XERROR: parse error
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'script' && end($this->stack)->tagName === 'script' &&
- // XDOM
- end($this->stack)->namespaceURI === self::NS_SVG) {
- array_pop($this->stack);
- // a bunch of script running mumbo jumbo
- } elseif (
- ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- ((
- $token['name'] !== 'mglyph' &&
- $token['name'] !== 'malignmark' &&
- // XDOM
- end($this->stack)->namespaceURI === self::NS_MATHML &&
- in_array(end($this->stack)->tagName, array('mi', 'mo', 'mn', 'ms', 'mtext'))
- ) ||
- (
- $token['name'] === 'svg' &&
- // XDOM
- end($this->stack)->namespaceURI === self::NS_MATHML &&
- end($this->stack)->tagName === 'annotation-xml'
- ) ||
- (
- // XDOM
- end($this->stack)->namespaceURI === self::NS_SVG &&
- in_array(end($this->stack)->tagName, array('foreignObject', 'desc', 'title'))
- ) ||
- (
- // XSKETCHY && XDOM
- end($this->stack)->namespaceURI === self::NS_HTML
- ))
- ) || $token['type'] === HTML5_Tokenizer::ENDTAG
- ) {
- $this->processWithRulesFor($token, $this->secondary_mode);
- /* If, after doing so, the insertion mode is still "in foreign
- * content", but there is no element in scope that has a namespace
- * other than the HTML namespace, switch the insertion mode to the
- * secondary insertion mode. */
- if ($this->mode === self::IN_FOREIGN_CONTENT) {
- $found = false;
- // this basically duplicates elementInScope()
- for ($i = count($this->stack) - 1; $i >= 0; $i--) {
- // XDOM
- $node = $this->stack[$i];
- if ($node->namespaceURI !== self::NS_HTML) {
- $found = true;
- break;
- } elseif (in_array($node->tagName, array('table', 'html',
- 'applet', 'caption', 'td', 'th', 'button', 'marquee',
- 'object')) || ($node->tagName === 'foreignObject' &&
- $node->namespaceURI === self::NS_SVG)) {
- break;
- }
- }
- if (!$found) {
- $this->mode = $this->secondary_mode;
- }
- }
- } elseif ($token['type'] === HTML5_Tokenizer::EOF || (
- $token['type'] === HTML5_Tokenizer::STARTTAG &&
- (in_array($token['name'], array('b', "big", "blockquote", "body", "br",
- "center", "code", "dc", "dd", "div", "dl", "ds", "dt", "em", "embed", "h1", "h2",
- "h3", "h4", "h5", "h6", "head", "hr", "i", "img", "li", "listing",
- "menu", "meta", "nobr", "ol", "p", "pre", "ruby", "s", "small",
- "span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul",
- "var")) || ($token['name'] === 'font' && ($this->getAttr($token, 'color') ||
- $this->getAttr($token, 'face') || $this->getAttr($token, 'size')))))) {
- // XERROR: parse error
- do {
- $node = array_pop($this->stack);
- // XDOM
- } while ($node->namespaceURI !== self::NS_HTML);
- $this->stack[] = $node;
- $this->mode = $this->secondary_mode;
- $this->emitToken($token);
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG) {
- static $svg_lookup = array(
- 'altglyph' => 'altGlyph',
- 'altglyphdef' => 'altGlyphDef',
- 'altglyphitem' => 'altGlyphItem',
- 'animatecolor' => 'animateColor',
- 'animatemotion' => 'animateMotion',
- 'animatetransform' => 'animateTransform',
- 'clippath' => 'clipPath',
- 'feblend' => 'feBlend',
- 'fecolormatrix' => 'feColorMatrix',
- 'fecomponenttransfer' => 'feComponentTransfer',
- 'fecomposite' => 'feComposite',
- 'feconvolvematrix' => 'feConvolveMatrix',
- 'fediffuselighting' => 'feDiffuseLighting',
- 'fedisplacementmap' => 'feDisplacementMap',
- 'fedistantlight' => 'feDistantLight',
- 'feflood' => 'feFlood',
- 'fefunca' => 'feFuncA',
- 'fefuncb' => 'feFuncB',
- 'fefuncg' => 'feFuncG',
- 'fefuncr' => 'feFuncR',
- 'fegaussianblur' => 'feGaussianBlur',
- 'feimage' => 'feImage',
- 'femerge' => 'feMerge',
- 'femergenode' => 'feMergeNode',
- 'femorphology' => 'feMorphology',
- 'feoffset' => 'feOffset',
- 'fepointlight' => 'fePointLight',
- 'fespecularlighting' => 'feSpecularLighting',
- 'fespotlight' => 'feSpotLight',
- 'fetile' => 'feTile',
- 'feturbulence' => 'feTurbulence',
- 'foreignobject' => 'foreignObject',
- 'glyphref' => 'glyphRef',
- 'lineargradient' => 'linearGradient',
- 'radialgradient' => 'radialGradient',
- 'textpath' => 'textPath',
- );
- // XDOM
- $current = end($this->stack);
- if ($current->namespaceURI === self::NS_MATHML) {
- $token = $this->adjustMathMLAttributes($token);
- }
- if ($current->namespaceURI === self::NS_SVG &&
- isset($svg_lookup[$token['name']])) {
- $token['name'] = $svg_lookup[$token['name']];
- }
- if ($current->namespaceURI === self::NS_SVG) {
- $token = $this->adjustSVGAttributes($token);
- }
- $token = $this->adjustForeignAttributes($token);
- $this->insertForeignElement($token, $current->namespaceURI);
- if (isset($token['self-closing'])) {
- array_pop($this->stack);
- // XERROR: acknowledge self-closing flag
- }
- }
- break;
-
- case self::AFTER_BODY:
- /* Handle the token as follows: */
-
- /* A character token that is one of one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- or U+0020 SPACE */
- if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Process the token as it would be processed if the insertion mode
- was "in body". */
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the first element in the stack of open
- elements (the html element), with the data attribute set to the
- data given in the comment token. */
- // XDOM
- $comment = $this->dom->createComment($token['data']);
- $this->stack[0]->appendChild($comment);
-
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // parse error
-
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* An end tag with the tag name "html" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'html') {
- /* If the parser was originally created as part of the HTML
- * fragment parsing algorithm, this is a parse error; ignore
- * the token. (fragment case) */
- $this->ignored = true;
- // XERROR: implement this
-
- $this->mode = self::AFTER_AFTER_BODY;
-
- } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
- /* Stop parsing */
-
- /* Anything else */
- } else {
- /* Parse error. Set the insertion mode to "in body" and reprocess
- the token. */
- $this->mode = self::IN_BODY;
- $this->emitToken($token);
- }
- break;
-
- case self::IN_FRAMESET:
- /* Handle the token as follows: */
-
- /* A character token that is one of one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
- if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Append the character to the current node. */
- $this->insertText($token['data']);
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the current node with the data
- attribute set to the data given in the comment token. */
- $this->insertComment($token['data']);
-
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // parse error
-
- /* A start tag with the tag name "frameset" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'frameset') {
- $this->insertElement($token);
-
- /* An end tag with the tag name "frameset" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'frameset') {
- /* If the current node is the root html element, then this is a
- parse error; ignore the token. (fragment case) */
- if (end($this->stack)->tagName === 'html') {
- $this->ignored = true;
- // Parse error
-
- } else {
- /* Otherwise, pop the current node from the stack of open
- elements. */
- array_pop($this->stack);
-
- /* If the parser was not originally created as part of the HTML
- * fragment parsing algorithm (fragment case), and the current
- * node is no longer a frameset element, then switch the
- * insertion mode to "after frameset". */
- $this->mode = self::AFTER_FRAMESET;
- }
-
- /* A start tag with the tag name "frame" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'frame') {
- /* Insert an HTML element for the token. */
- $this->insertElement($token);
-
- /* Immediately pop the current node off the stack of open elements. */
- array_pop($this->stack);
-
- // XERROR: Acknowledge the token's self-closing flag, if it is set.
-
- /* A start tag with the tag name "noframes" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'noframes') {
- /* Process the token using the rules for the "in head" insertion mode. */
- $this->processwithRulesFor($token, self::IN_HEAD);
-
- } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
- // XERROR: If the current node is not the root html element, then this is a parse error.
- /* Stop parsing */
- /* Anything else */
- } else {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
- }
- break;
-
- case self::AFTER_FRAMESET:
- /* Handle the token as follows: */
-
- /* A character token that is one of one of U+0009 CHARACTER TABULATION,
- U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
- U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
- if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
- /* Append the character to the current node. */
- $this->insertText($token['data']);
-
- /* A comment token */
- } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the current node with the data
- attribute set to the data given in the comment token. */
- $this->insertComment($token['data']);
-
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
- // parse error
-
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* An end tag with the tag name "html" */
- } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
- $token['name'] === 'html') {
- $this->mode = self::AFTER_AFTER_FRAMESET;
-
- /* A start tag with the tag name "noframes" */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
- $token['name'] === 'noframes') {
- $this->processWithRulesFor($token, self::IN_HEAD);
-
- } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
- /* Stop parsing */
-
- /* Anything else */
- } else {
- /* Parse error. Ignore the token. */
- $this->ignored = true;
- }
- break;
-
- case self::AFTER_AFTER_BODY:
- /* A comment token */
- if ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the Document object with the data
- attribute set to the data given in the comment token. */
- // XDOM
- $comment = $this->dom->createComment($token['data']);
- $this->dom->appendChild($comment);
-
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE ||
- $token['type'] === HTML5_Tokenizer::SPACECHARACTER ||
- ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) {
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* An end-of-file token */
- } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
- /* OMG DONE!! */
- } else {
- // parse error
- $this->mode = self::IN_BODY;
- $this->emitToken($token);
- }
- break;
-
- case self::AFTER_AFTER_FRAMESET:
- /* A comment token */
- if ($token['type'] === HTML5_Tokenizer::COMMENT) {
- /* Append a Comment node to the Document object with the data
- attribute set to the data given in the comment token. */
- // XDOM
- $comment = $this->dom->createComment($token['data']);
- $this->dom->appendChild($comment);
- } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE ||
- $token['type'] === HTML5_Tokenizer::SPACECHARACTER ||
- ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) {
- $this->processWithRulesFor($token, self::IN_BODY);
-
- /* An end-of-file token */
- } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
- /* OMG DONE!! */
- } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'nofrmaes') {
- $this->processWithRulesFor($token, self::IN_HEAD);
- } else {
- // parse error
- }
- break;
- }
- }
-
- private function insertElement($token, $append = true) {
- $el = $this->dom->createElementNS(self::NS_HTML, $token['name']);
-
- if (!empty($token['attr'])) {
- foreach ($token['attr'] as $attr) {
- if (!$el->hasAttribute($attr['name']) && preg_match("/^[a-zA-Z_:]/", $attr['name'])) {
- $el->setAttribute($attr['name'], $attr['value']);
- }
- }
- }
- if ($append) {
- $this->appendToRealParent($el);
- $this->stack[] = $el;
- }
-
- return $el;
- }
-
- /**
- * @param $data
- */
- private function insertText($data) {
- if ($data === '') {
- return;
- }
- if ($this->ignore_lf_token) {
- if ($data[0] === "\n") {
- $data = substr($data, 1);
- if ($data === false) {
- return;
- }
- }
- }
- $text = $this->dom->createTextNode($data);
- $this->appendToRealParent($text);
- }
-
- /**
- * @param $data
- */
- private function insertComment($data) {
- $comment = $this->dom->createComment($data);
- $this->appendToRealParent($comment);
- }
-
- /**
- * @param $node
- */
- private function appendToRealParent($node) {
- // this is only for the foster_parent case
- /* If the current node is a table, tbody, tfoot, thead, or tr
- element, then, whenever a node would be inserted into the current
- node, it must instead be inserted into the foster parent element. */
- if (
- !$this->foster_parent ||
- !in_array(
- end($this->stack)->tagName,
- array('table', 'tbody', 'tfoot', 'thead', 'tr')
- )
- ) {
- end($this->stack)->appendChild($node);
- } else {
- $this->fosterParent($node);
- }
- }
-
- /**
- * @param $el
- * @param int $scope
- * @return bool|null
- */
- private function elementInScope($el, $scope = self::SCOPE) {
- if (is_array($el)) {
- foreach($el as $element) {
- if ($this->elementInScope($element, $scope)) {
- return true;
- }
- }
-
- return false;
- }
-
- $leng = count($this->stack);
-
- for ($n = 0; $n < $leng; $n++) {
- /* 1. Initialise node to be the current node (the bottommost node of
- the stack). */
- $node = $this->stack[$leng - 1 - $n];
-
- if ($node->tagName === $el) {
- /* 2. If node is the target node, terminate in a match state. */
- return true;
-
- // We've expanded the logic for these states a little differently;
- // Hixie's refactoring into "specific scope" is more general, but
- // this "gets the job done"
-
- // these are the common states for all scopes
- } elseif ($node->tagName === 'table' || $node->tagName === 'html') {
- return false;
-
- // these are valid for "in scope" and "in list item scope"
- } elseif ($scope !== self::SCOPE_TABLE &&
- (in_array($node->tagName, array('applet', 'caption', 'td',
- 'th', 'button', 'marquee', 'object')) ||
- $node->tagName === 'foreignObject' && $node->namespaceURI === self::NS_SVG)) {
- return false;
-
-
- // these are valid for "in list item scope"
- } elseif ($scope === self::SCOPE_LISTITEM && in_array($node->tagName, array('ol', 'ul'))) {
- return false;
- }
-
- /* Otherwise, set node to the previous entry in the stack of open
- elements and return to step 2. (This will never fail, since the loop
- will always terminate in the previous step if the top of the stack
- is reached.) */
- }
-
- // To fix warning. This never happens or should return true/false
- return null;
- }
-
- /**
- * @return bool
- */
- private function reconstructActiveFormattingElements() {
- /* 1. If there are no entries in the list of active formatting elements,
- then there is nothing to reconstruct; stop this algorithm. */
- $formatting_elements = count($this->a_formatting);
-
- if ($formatting_elements === 0) {
- return false;
- }
-
- /* 3. Let entry be the last (most recently added) element in the list
- of active formatting elements. */
- $entry = end($this->a_formatting);
-
- /* 2. If the last (most recently added) entry in the list of active
- formatting elements is a marker, or if it is an element that is in the
- stack of open elements, then there is nothing to reconstruct; stop this
- algorithm. */
- if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
- return false;
- }
-
- for ($a = $formatting_elements - 1; $a >= 0; true) {
- /* 4. If there are no entries before entry in the list of active
- formatting elements, then jump to step 8. */
- if ($a === 0) {
- $step_seven = false;
- break;
- }
-
- /* 5. Let entry be the entry one earlier than entry in the list of
- active formatting elements. */
- $a--;
- $entry = $this->a_formatting[$a];
-
- /* 6. If entry is neither a marker nor an element that is also in
- thetack of open elements, go to step 4. */
- if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
- break;
- }
- }
-
- while (true) {
- /* 7. Let entry be the element one later than entry in the list of
- active formatting elements. */
- if (isset($step_seven) && $step_seven === true) {
- $a++;
- $entry = $this->a_formatting[$a];
- }
-
- /* 8. Perform a shallow clone of the element entry to obtain clone. */
- $clone = $entry->cloneNode();
-
- /* 9. Append clone to the current node and push it onto the stack
- of open elements so that it is the new current node. */
- $this->appendToRealParent($clone);
- $this->stack[] = $clone;
-
- /* 10. Replace the entry for entry in the list with an entry for
- clone. */
- $this->a_formatting[$a] = $clone;
-
- /* 11. If the entry for clone in the list of active formatting
- elements is not the last entry in the list, return to step 7. */
- if (end($this->a_formatting) !== $clone) {
- $step_seven = true;
- } else {
- break;
- }
- }
-
- // Return value not in use ATM. Would just make sense to also return true here.
- return true;
- }
-
- /**
- *
- */
- private function clearTheActiveFormattingElementsUpToTheLastMarker() {
- /* When the steps below require the UA to clear the list of active
- formatting elements up to the last marker, the UA must perform the
- following steps: */
-
- while (true) {
- /* 1. Let entry be the last (most recently added) entry in the list
- of active formatting elements. */
- $entry = end($this->a_formatting);
-
- /* 2. Remove entry from the list of active formatting elements. */
- array_pop($this->a_formatting);
-
- /* 3. If entry was a marker, then stop the algorithm at this point.
- The list has been cleared up to the last marker. */
- if ($entry === self::MARKER) {
- break;
- }
- }
- }
-
- /**
- * @param array $exclude
- */
- private function generateImpliedEndTags($exclude = array()) {
- /* When the steps below require the UA to generate implied end tags,
- * then, while the current node is a dc element, a dd element, a ds
- * element, a dt element, an li element, an option element, an optgroup
- * element, a p element, an rp element, or an rt element, the UA must
- * pop the current node off the stack of open elements. */
- $node = end($this->stack);
- $elements = array_diff(array('dc', 'dd', 'ds', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);
-
- while (in_array(end($this->stack)->tagName, $elements)) {
- array_pop($this->stack);
- }
- }
-
- /**
- * @param $node
- * @return int
- */
- private function getElementCategory($node) {
- if (!is_object($node)) {
- debug_print_backtrace();
- }
- $name = $node->tagName;
- if (in_array($name, $this->special)) {
- return self::SPECIAL;
- } elseif (in_array($name, $this->scoping)) {
- return self::SCOPING;
- } elseif (in_array($name, $this->formatting)) {
- return self::FORMATTING;
- } else {
- return self::PHRASING;
- }
- }
-
- /**
- * @param $elements
- */
- private function clearStackToTableContext($elements) {
- /* When the steps above require the UA to clear the stack back to a
- table context, it means that the UA must, while the current node is not
- a table element or an html element, pop elements from the stack of open
- elements. */
- while (true) {
- $name = end($this->stack)->tagName;
-
- if (in_array($name, $elements)) {
- break;
- } else {
- array_pop($this->stack);
- }
- }
- }
-
- /**
- * @param null $context
- */
- private function resetInsertionMode($context = null) {
- /* 1. Let last be false. */
- $last = false;
- $leng = count($this->stack);
-
- for ($n = $leng - 1; $n >= 0; $n--) {
- /* 2. Let node be the last node in the stack of open elements. */
- $node = $this->stack[$n];
-
- /* 3. If node is the first node in the stack of open elements, then
- * set last to true and set node to the context element. (fragment
- * case) */
- if ($this->stack[0]->isSameNode($node)) {
- $last = true;
- $node = $context;
- }
-
- /* 4. If node is a select element, then switch the insertion mode to
- "in select" and abort these steps. (fragment case) */
- if ($node->tagName === 'select') {
- $this->mode = self::IN_SELECT;
- break;
-
- /* 5. If node is a td or th element, then switch the insertion mode
- to "in cell" and abort these steps. */
- } elseif ($node->tagName === 'td' || $node->nodeName === 'th') {
- $this->mode = self::IN_CELL;
- break;
-
- /* 6. If node is a tr element, then switch the insertion mode to
- "in row" and abort these steps. */
- } elseif ($node->tagName === 'tr') {
- $this->mode = self::IN_ROW;
- break;
-
- /* 7. If node is a tbody, thead, or tfoot element, then switch the
- insertion mode to "in table body" and abort these steps. */
- } elseif (in_array($node->tagName, array('tbody', 'thead', 'tfoot'))) {
- $this->mode = self::IN_TABLE_BODY;
- break;
-
- /* 8. If node is a caption element, then switch the insertion mode
- to "in caption" and abort these steps. */
- } elseif ($node->tagName === 'caption') {
- $this->mode = self::IN_CAPTION;
- break;
-
- /* 9. If node is a colgroup element, then switch the insertion mode
- to "in column group" and abort these steps. (innerHTML case) */
- } elseif ($node->tagName === 'colgroup') {
- $this->mode = self::IN_COLUMN_GROUP;
- break;
-
- /* 10. If node is a table element, then switch the insertion mode
- to "in table" and abort these steps. */
- } elseif ($node->tagName === 'table') {
- $this->mode = self::IN_TABLE;
- break;
-
- /* 11. If node is an element from the MathML namespace or the SVG
- * namespace, then switch the insertion mode to "in foreign
- * content", let the secondary insertion mode be "in body", and
- * abort these steps. */
- } elseif ($node->namespaceURI === self::NS_SVG ||
- $node->namespaceURI === self::NS_MATHML) {
- $this->mode = self::IN_FOREIGN_CONTENT;
- $this->secondary_mode = self::IN_BODY;
- break;
-
- /* 12. If node is a head element, then switch the insertion mode
- to "in body" ("in body"! not "in head"!) and abort these steps.
- (fragment case) */
- } elseif ($node->tagName === 'head') {
- $this->mode = self::IN_BODY;
- break;
-
- /* 13. If node is a body element, then switch the insertion mode to
- "in body" and abort these steps. */
- } elseif ($node->tagName === 'body') {
- $this->mode = self::IN_BODY;
- break;
-
- /* 14. If node is a frameset element, then switch the insertion
- mode to "in frameset" and abort these steps. (fragment case) */
- } elseif ($node->tagName === 'frameset') {
- $this->mode = self::IN_FRAMESET;
- break;
-
- /* 15. If node is an html element, then: if the head element
- pointer is null, switch the insertion mode to "before head",
- otherwise, switch the insertion mode to "after head". In either
- case, abort these steps. (fragment case) */
- } elseif ($node->tagName === 'html') {
- $this->mode = ($this->head_pointer === null)
- ? self::BEFORE_HEAD
- : self::AFTER_HEAD;
-
- break;
-
- /* 16. If last is true, then set the insertion mode to "in body"
- and abort these steps. (fragment case) */
- } elseif ($last) {
- $this->mode = self::IN_BODY;
- break;
- }
- }
- }
-
- /**
- *
- */
- private function closeCell() {
- /* If the stack of open elements has a td or th element in table scope,
- then act as if an end tag token with that tag name had been seen. */
- foreach (array('td', 'th') as $cell) {
- if ($this->elementInScope($cell, self::SCOPE_TABLE)) {
- $this->emitToken(array(
- 'name' => $cell,
- 'type' => HTML5_Tokenizer::ENDTAG
- ));
-
- break;
- }
- }
- }
-
- /**
- * @param $token
- * @param $mode
- */
- private function processWithRulesFor($token, $mode) {
- /* "using the rules for the m insertion mode", where m is one of these
- * modes, the user agent must use the rules described under the m
- * insertion mode's section, but must leave the insertion mode
- * unchanged unless the rules in m themselves switch the insertion mode
- * to a new value. */
- $this->emitToken($token, $mode);
- }
-
- /**
- * @param $token
- */
- private function insertCDATAElement($token) {
- $this->insertElement($token);
- $this->original_mode = $this->mode;
- $this->mode = self::IN_CDATA_RCDATA;
- $this->content_model = HTML5_Tokenizer::CDATA;
- }
-
- /**
- * @param $token
- */
- private function insertRCDATAElement($token) {
- $this->insertElement($token);
- $this->original_mode = $this->mode;
- $this->mode = self::IN_CDATA_RCDATA;
- $this->content_model = HTML5_Tokenizer::RCDATA;
- }
-
- /**
- * @param $token
- * @param $key
- * @return bool
- */
- private function getAttr($token, $key) {
- if (!isset($token['attr'])) {
- return false;
- }
- $ret = false;
- foreach ($token['attr'] as $keypair) {
- if ($keypair['name'] === $key) {
- $ret = $keypair['value'];
- }
- }
- return $ret;
- }
-
- /**
- * @return mixed
- */
- private function getCurrentTable() {
- /* The current table is the last table element in the stack of open
- * elements, if there is one. If there is no table element in the stack
- * of open elements (fragment case), then the current table is the
- * first element in the stack of open elements (the html element). */
- for ($i = count($this->stack) - 1; $i >= 0; $i--) {
- if ($this->stack[$i]->tagName === 'table') {
- return $this->stack[$i];
- }
- }
- return $this->stack[0];
- }
-
- /**
- * @return mixed
- */
- private function getFosterParent() {
- /* The foster parent element is the parent element of the last
- table element in the stack of open elements, if there is a
- table element and it has such a parent element. If there is no
- table element in the stack of open elements (innerHTML case),
- then the foster parent element is the first element in the
- stack of open elements (the html element). Otherwise, if there
- is a table element in the stack of open elements, but the last
- table element in the stack of open elements has no parent, or
- its parent node is not an element, then the foster parent
- element is the element before the last table element in the
- stack of open elements. */
- for ($n = count($this->stack) - 1; $n >= 0; $n--) {
- if ($this->stack[$n]->tagName === 'table') {
- $table = $this->stack[$n];
- break;
- }
- }
-
- if (isset($table) && $table->parentNode !== null) {
- return $table->parentNode;
-
- } elseif (!isset($table)) {
- return $this->stack[0];
-
- } elseif (isset($table) && ($table->parentNode === null ||
- $table->parentNode->nodeType !== XML_ELEMENT_NODE)) {
- return $this->stack[$n - 1];
- }
-
- return null;
- }
-
- /**
- * @param $node
- */
- public function fosterParent($node) {
- $foster_parent = $this->getFosterParent();
- $table = $this->getCurrentTable(); // almost equivalent to last table element, except it can be html
- /* When a node node is to be foster parented, the node node must be
- * be inserted into the foster parent element. */
- /* If the foster parent element is the parent element of the last table
- * element in the stack of open elements, then node must be inserted
- * immediately before the last table element in the stack of open
- * elements in the foster parent element; otherwise, node must be
- * appended to the foster parent element. */
- if ($table->tagName === 'table' && $table->parentNode->isSameNode($foster_parent)) {
- $foster_parent->insertBefore($node, $table);
- } else {
- $foster_parent->appendChild($node);
- }
- }
-
- /**
- * For debugging, prints the stack
- */
- private function printStack() {
- $names = array();
- foreach ($this->stack as $i => $element) {
- $names[] = $element->tagName;
- }
- echo " -> stack [" . implode(', ', $names) . "]\n";
- }
-
- /**
- * For debugging, prints active formatting elements
- */
- private function printActiveFormattingElements() {
- if (!$this->a_formatting) {
- return;
- }
- $names = array();
- foreach ($this->a_formatting as $node) {
- if ($node === self::MARKER) {
- $names[] = 'MARKER';
- } else {
- $names[] = $node->tagName;
- }
- }
- echo " -> active formatting [" . implode(', ', $names) . "]\n";
- }
-
- /**
- * @return bool
- */
- public function currentTableIsTainted() {
- return !empty($this->getCurrentTable()->tainted);
- }
-
- /**
- * Sets up the tree constructor for building a fragment.
- *
- * @param null $context
- */
- public function setupContext($context = null) {
- $this->fragment = true;
- if ($context) {
- $context = $this->dom->createElementNS(self::NS_HTML, $context);
- /* 4.1. Set the HTML parser's tokenization stage's content model
- * flag according to the context element, as follows: */
- switch ($context->tagName) {
- case 'title': case 'textarea':
- $this->content_model = HTML5_Tokenizer::RCDATA;
- break;
- case 'style': case 'script': case 'xmp': case 'iframe':
- case 'noembed': case 'noframes':
- $this->content_model = HTML5_Tokenizer::CDATA;
- break;
- case 'noscript':
- // XSCRIPT: assuming scripting is enabled
- $this->content_model = HTML5_Tokenizer::CDATA;
- break;
- case 'plaintext':
- $this->content_model = HTML5_Tokenizer::PLAINTEXT;
- break;
- }
- /* 4.2. Let root be a new html element with no attributes. */
- $root = $this->dom->createElementNS(self::NS_HTML, 'html');
- $this->root = $root;
- /* 4.3 Append the element root to the Document node created above. */
- $this->dom->appendChild($root);
- /* 4.4 Set up the parser's stack of open elements so that it
- * contains just the single element root. */
- $this->stack = array($root);
- /* 4.5 Reset the parser's insertion mode appropriately. */
- $this->resetInsertionMode($context);
- /* 4.6 Set the parser's form element pointer to the nearest node
- * to the context element that is a form element (going straight up
- * the ancestor chain, and including the element itself, if it is a
- * form element), or, if there is no such form element, to null. */
- $node = $context;
- do {
- if ($node->tagName === 'form') {
- $this->form_pointer = $node;
- break;
- }
- } while ($node = $node->parentNode);
- }
- }
-
- /**
- * @param $token
- * @return mixed
- */
- public function adjustMathMLAttributes($token) {
- foreach ($token['attr'] as &$kp) {
- if ($kp['name'] === 'definitionurl') {
- $kp['name'] = 'definitionURL';
- }
- }
- return $token;
- }
-
- /**
- * @param $token
- * @return mixed
- */
- public function adjustSVGAttributes($token) {
- static $lookup = array(
- 'attributename' => 'attributeName',
- 'attributetype' => 'attributeType',
- 'basefrequency' => 'baseFrequency',
- 'baseprofile' => 'baseProfile',
- 'calcmode' => 'calcMode',
- 'clippathunits' => 'clipPathUnits',
- 'contentscripttype' => 'contentScriptType',
- 'contentstyletype' => 'contentStyleType',
- 'diffuseconstant' => 'diffuseConstant',
- 'edgemode' => 'edgeMode',
- 'externalresourcesrequired' => 'externalResourcesRequired',
- 'filterres' => 'filterRes',
- 'filterunits' => 'filterUnits',
- 'glyphref' => 'glyphRef',
- 'gradienttransform' => 'gradientTransform',
- 'gradientunits' => 'gradientUnits',
- 'kernelmatrix' => 'kernelMatrix',
- 'kernelunitlength' => 'kernelUnitLength',
- 'keypoints' => 'keyPoints',
- 'keysplines' => 'keySplines',
- 'keytimes' => 'keyTimes',
- 'lengthadjust' => 'lengthAdjust',
- 'limitingconeangle' => 'limitingConeAngle',
- 'markerheight' => 'markerHeight',
- 'markerunits' => 'markerUnits',
- 'markerwidth' => 'markerWidth',
- 'maskcontentunits' => 'maskContentUnits',
- 'maskunits' => 'maskUnits',
- 'numoctaves' => 'numOctaves',
- 'pathlength' => 'pathLength',
- 'patterncontentunits' => 'patternContentUnits',
- 'patterntransform' => 'patternTransform',
- 'patternunits' => 'patternUnits',
- 'pointsatx' => 'pointsAtX',
- 'pointsaty' => 'pointsAtY',
- 'pointsatz' => 'pointsAtZ',
- 'preservealpha' => 'preserveAlpha',
- 'preserveaspectratio' => 'preserveAspectRatio',
- 'primitiveunits' => 'primitiveUnits',
- 'refx' => 'refX',
- 'refy' => 'refY',
- 'repeatcount' => 'repeatCount',
- 'repeatdur' => 'repeatDur',
- 'requiredextensions' => 'requiredExtensions',
- 'requiredfeatures' => 'requiredFeatures',
- 'specularconstant' => 'specularConstant',
- 'specularexponent' => 'specularExponent',
- 'spreadmethod' => 'spreadMethod',
- 'startoffset' => 'startOffset',
- 'stddeviation' => 'stdDeviation',
- 'stitchtiles' => 'stitchTiles',
- 'surfacescale' => 'surfaceScale',
- 'systemlanguage' => 'systemLanguage',
- 'tablevalues' => 'tableValues',
- 'targetx' => 'targetX',
- 'targety' => 'targetY',
- 'textlength' => 'textLength',
- 'viewbox' => 'viewBox',
- 'viewtarget' => 'viewTarget',
- 'xchannelselector' => 'xChannelSelector',
- 'ychannelselector' => 'yChannelSelector',
- 'zoomandpan' => 'zoomAndPan',
- );
- foreach ($token['attr'] as &$kp) {
- if (isset($lookup[$kp['name']])) {
- $kp['name'] = $lookup[$kp['name']];
- }
- }
- return $token;
- }
-
- /**
- * @param $token
- * @return mixed
- */
- public function adjustForeignAttributes($token) {
- static $lookup = array(
- 'xlink:actuate' => array('xlink', 'actuate', self::NS_XLINK),
- 'xlink:arcrole' => array('xlink', 'arcrole', self::NS_XLINK),
- 'xlink:href' => array('xlink', 'href', self::NS_XLINK),
- 'xlink:role' => array('xlink', 'role', self::NS_XLINK),
- 'xlink:show' => array('xlink', 'show', self::NS_XLINK),
- 'xlink:title' => array('xlink', 'title', self::NS_XLINK),
- 'xlink:type' => array('xlink', 'type', self::NS_XLINK),
- 'xml:base' => array('xml', 'base', self::NS_XML),
- 'xml:lang' => array('xml', 'lang', self::NS_XML),
- 'xml:space' => array('xml', 'space', self::NS_XML),
- 'xmlns' => array(null, 'xmlns', self::NS_XMLNS),
- 'xmlns:xlink' => array('xmlns', 'xlink', self::NS_XMLNS),
- );
- foreach ($token['attr'] as &$kp) {
- if (isset($lookup[$kp['name']])) {
- $kp['name'] = $lookup[$kp['name']];
- }
- }
- return $token;
- }
-
- /**
- * @param $token
- * @param $namespaceURI
- */
- public function insertForeignElement($token, $namespaceURI) {
- $el = $this->dom->createElementNS($namespaceURI, $token['name']);
-
- if (!empty($token['attr'])) {
- foreach ($token['attr'] as $kp) {
- $attr = $kp['name'];
- if (is_array($attr)) {
- $ns = $attr[2];
- $attr = $attr[1];
- } else {
- $ns = self::NS_HTML;
- }
- if (!$el->hasAttributeNS($ns, $attr)) {
- // XSKETCHY: work around godawful libxml bug
- if ($ns === self::NS_XLINK) {
- $el->setAttribute('xlink:'.$attr, $kp['value']);
- } elseif ($ns === self::NS_HTML) {
- // Another godawful libxml bug
- $el->setAttribute($attr, $kp['value']);
- } else {
- $el->setAttributeNS($ns, $attr, $kp['value']);
- }
- }
- }
- }
- $this->appendToRealParent($el);
- $this->stack[] = $el;
- // XERROR: see below
- /* If the newly created element has an xmlns attribute in the XMLNS
- * namespace whose value is not exactly the same as the element's
- * namespace, that is a parse error. Similarly, if the newly created
- * element has an xmlns:xlink attribute in the XMLNS namespace whose
- * value is not the XLink Namespace, that is a parse error. */
- }
-
- /**
- * @return DOMDocument|DOMNodeList
- */
- public function save() {
- $this->dom->normalize();
- if (!$this->fragment) {
- return $this->dom;
- } else {
- if ($this->root) {
- return $this->root->childNodes;
- } else {
- return $this->dom->childNodes;
- }
- }
- }
-}
-
diff --git a/library/vendor/dompdf/lib/html5lib/named-character-references.ser b/library/vendor/dompdf/lib/html5lib/named-character-references.ser
deleted file mode 100644
index e3ae05020..000000000
--- a/library/vendor/dompdf/lib/html5lib/named-character-references.ser
+++ /dev/null
@@ -1 +0,0 @@
-a:52:{s:1:"A";a:16:{s:1:"E";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:198;}s:9:"codepoint";i:198;}}}}s:1:"M";a:1:{s:1:"P";a:2:{s:1:";";a:1:{s:9:"codepoint";i:38;}s:9:"codepoint";i:38;}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:193;}s:9:"codepoint";i:193;}}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:258;}}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:194;}s:9:"codepoint";i:194;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1040;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120068;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:192;}s:9:"codepoint";i:192;}}}}}s:1:"l";a:1:{s:1:"p";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:913;}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:256;}}}}}s:1:"n";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10835;}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:260;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120120;}}}}s:1:"p";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"y";a:1:{s:1:"F";a:1:{s:1:"u";a:1:{s:1:"n";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8289;}}}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:197;}s:9:"codepoint";i:197;}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119964;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8788;}}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:195;}s:9:"codepoint";i:195;}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:196;}s:9:"codepoint";i:196;}}}}s:1:"B";a:8:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"s";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}}}}}s:1:"r";a:2:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10983;}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8966;}}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1041;}}}s:1:"e";a:3:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8757;}}}}}}s:1:"r";a:1:{s:1:"n";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8492;}}}}}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:914;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120069;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120121;}}}}s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:728;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8492;}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8782;}}}}}}}s:1:"C";a:14:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1063;}}}}s:1:"O";a:1:{s:1:"P";a:1:{s:1:"Y";a:2:{s:1:";";a:1:{s:9:"codepoint";i:169;}s:9:"codepoint";i:169;}}}s:1:"a";a:3:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:262;}}}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8914;}s:1:"i";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"D";a:1:{s:1:"i";a:1:{s:1:"f";a:1:{s:1:"f";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8517;}}}}}}}}}}}}}}}}}}}s:1:"y";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"y";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8493;}}}}}}}s:1:"c";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:268;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:199;}s:9:"codepoint";i:199;}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:264;}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8752;}}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:266;}}}}s:1:"e";a:2:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:184;}}}}}}s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:183;}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8493;}}}s:1:"h";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:935;}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"l";a:1:{s:1:"e";a:4:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8857;}}}}s:1:"M";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8854;}}}}}}s:1:"P";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8853;}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8855;}}}}}}}}}}}s:1:"l";a:1:{s:1:"o";a:2:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"w";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"C";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8754;}}}}}}}}}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"C";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"l";a:1:{s:1:"y";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"Q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8221;}}}}}}}}}}}}s:1:"Q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8217;}}}}}}}}}}}}}}}s:1:"o";a:4:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8759;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10868;}}}}}s:1:"n";a:3:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8801;}}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8751;}}}}s:1:"t";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8750;}}}}}}}}}}}}}}s:1:"p";a:2:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8450;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:"u";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8720;}}}}}}}}s:1:"u";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"C";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"w";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"C";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8755;}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10799;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119966;}}}}s:1:"u";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8915;}s:1:"C";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8781;}}}}}}}s:1:"D";a:11:{s:1:"D";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8517;}s:1:"o";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"h";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10513;}}}}}}}}s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1026;}}}}s:1:"S";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1029;}}}}s:1:"Z";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1039;}}}}s:1:"a";a:3:{s:1:"g";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8225;}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8609;}}}s:1:"s";a:1:{s:1:"h";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10980;}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:270;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1044;}}}s:1:"e";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8711;}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:916;}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120071;}}}s:1:"i";a:2:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:4:{s:1:"A";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:180;}}}}}}s:1:"D";a:1:{s:1:"o";a:2:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:729;}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"A";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:733;}}}}}}}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:96;}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:732;}}}}}}}}}}}}}}s:1:"m";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8900;}}}}}}s:1:"f";a:1:{s:1:"f";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8518;}}}}}}}}}}}}}s:1:"o";a:4:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120123;}}}s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:168;}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8412;}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8784;}}}}}}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:6:{s:1:"C";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8751;}}}}}}}}}}}}}}}}s:1:"D";a:1:{s:1:"o";a:2:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:168;}}s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8659;}}}}}}}}}}s:1:"L";a:2:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:3:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8656;}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8660;}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10980;}}}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:2:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10232;}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10234;}}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10233;}}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8658;}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8872;}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8657;}}}}}}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8661;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8741;}}}}}}}}}}}}}}}}s:1:"w";a:1:{s:1:"n";a:6:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8595;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10515;}}}}s:1:"U";a:1:{s:1:"p";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8693;}}}}}}}}}}}}}s:1:"B";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:785;}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:3:{s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10576;}}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10590;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8637;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10582;}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10591;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8641;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10583;}}}}}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8868;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8615;}}}}}}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8659;}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119967;}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:272;}}}}}}}s:1:"E";a:16:{s:1:"N";a:1:{s:1:"G";a:1:{s:1:";";a:1:{s:9:"codepoint";i:330;}}}s:1:"T";a:1:{s:1:"H";a:2:{s:1:";";a:1:{s:9:"codepoint";i:208;}s:9:"codepoint";i:208;}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:201;}s:9:"codepoint";i:201;}}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:282;}}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:202;}s:9:"codepoint";i:202;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1069;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:278;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120072;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:200;}s:9:"codepoint";i:200;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8712;}}}}}}}s:1:"m";a:2:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:274;}}}}s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:2:{s:1:"S";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"S";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9723;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"y";a:1:{s:1:"S";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"S";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9643;}}}}}}}}}}}}}}}}}}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:280;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120124;}}}}s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:917;}}}}}}}s:1:"q";a:1:{s:1:"u";a:2:{s:1:"a";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10869;}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8770;}}}}}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8652;}}}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8496;}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10867;}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:919;}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:203;}s:9:"codepoint";i:203;}}}s:1:"x";a:2:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8707;}}}}}s:1:"p";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8519;}}}}}}}}}}}}}s:1:"F";a:5:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1060;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120073;}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"d";a:2:{s:1:"S";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"S";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9724;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"y";a:1:{s:1:"S";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"S";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9642;}}}}}}}}}}}}}}}}}}}}}s:1:"o";a:3:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120125;}}}s:1:"r";a:1:{s:1:"A";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8704;}}}}}s:1:"u";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8497;}}}}}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8497;}}}}}s:1:"G";a:12:{s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1027;}}}}s:1:"T";a:2:{s:1:";";a:1:{s:9:"codepoint";i:62;}s:9:"codepoint";i:62;}s:1:"a";a:1:{s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:2:{s:1:";";a:1:{s:9:"codepoint";i:915;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:988;}}}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:286;}}}}}}s:1:"c";a:3:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:290;}}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:284;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1043;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:288;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120074;}}}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8921;}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120126;}}}}s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:6:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8805;}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8923;}}}}}}}}}}s:1:"F";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8807;}}}}}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10914;}}}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8823;}}}}}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10878;}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8819;}}}}}}}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119970;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8811;}}}s:1:"H";a:8:{s:1:"A";a:1:{s:1:"R";a:1:{s:1:"D";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1066;}}}}}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:711;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:94;}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:292;}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8460;}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"b";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8459;}}}}}}}}}}}}s:1:"o";a:2:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8461;}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"z";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"L";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9472;}}}}}}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8459;}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:294;}}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"p";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"H";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8782;}}}}}}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8783;}}}}}}}}}}s:1:"I";a:14:{s:1:"E";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1045;}}}}s:1:"J";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:306;}}}}}s:1:"O";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1025;}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:205;}s:9:"codepoint";i:205;}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:206;}s:9:"codepoint";i:206;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1048;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:304;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8465;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:204;}s:9:"codepoint";i:204;}}}}}s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8465;}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:298;}}}s:1:"g";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"y";a:1:{s:1:"I";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8520;}}}}}}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8658;}}}}}}}s:1:"n";a:2:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8748;}s:1:"e";a:2:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8747;}}}}}s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8898;}}}}}}}}}}}s:1:"v";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:"C";a:1:{s:1:"o";a:1:{s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8291;}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8290;}}}}}}}}}}}}}}s:1:"o";a:3:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:302;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120128;}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:921;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8464;}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:296;}}}}}}s:1:"u";a:2:{s:1:"k";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1030;}}}}s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:207;}s:9:"codepoint";i:207;}}}}s:1:"J";a:5:{s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:308;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1049;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120077;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120129;}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119973;}}}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1032;}}}}}}s:1:"u";a:1:{s:1:"k";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1028;}}}}}}s:1:"K";a:7:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1061;}}}}s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1036;}}}}s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:922;}}}}}s:1:"c";a:2:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:310;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1050;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120078;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120130;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119974;}}}}}s:1:"L";a:11:{s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1033;}}}}s:1:"T";a:2:{s:1:";";a:1:{s:9:"codepoint";i:60;}s:9:"codepoint";i:60;}s:1:"a";a:5:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:313;}}}}}s:1:"m";a:1:{s:1:"b";a:1:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:923;}}}}}s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10218;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8466;}}}}}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8606;}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:317;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:315;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1051;}}}s:1:"e";a:2:{s:1:"f";a:1:{s:1:"t";a:10:{s:1:"A";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10216;}}}}}}}}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8592;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8676;}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8646;}}}}}}}}}}}}}}}}s:1:"C";a:1:{s:1:"e";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8968;}}}}}}}}s:1:"D";a:1:{s:1:"o";a:2:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10214;}}}}}}}}}}}}s:1:"w";a:1:{s:1:"n";a:2:{s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10593;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8643;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10585;}}}}}}}}}}}}}}s:1:"F";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8970;}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8596;}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10574;}}}}}}}}}}}}s:1:"T";a:2:{s:1:"e";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8867;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8612;}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10586;}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8882;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10703;}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8884;}}}}}}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:3:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10577;}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10592;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8639;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10584;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8636;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10578;}}}}}}}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8656;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8660;}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"s";a:6:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8922;}}}}}}}}}}}}}s:1:"F";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8806;}}}}}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8822;}}}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10913;}}}}}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10877;}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8818;}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120079;}}}s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8920;}s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8666;}}}}}}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:319;}}}}}}s:1:"o";a:3:{s:1:"n";a:1:{s:1:"g";a:4:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10229;}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10231;}}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10230;}}}}}}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10232;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10234;}}}}}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10233;}}}}}}}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120131;}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"r";a:2:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8601;}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8600;}}}}}}}}}}}}}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8466;}}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8624;}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:321;}}}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8810;}}}s:1:"M";a:8:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10501;}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1052;}}}s:1:"e";a:2:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8287;}}}}}}}}}}s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8499;}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120080;}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"P";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8723;}}}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120132;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8499;}}}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:924;}}}s:1:"N";a:9:{s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1034;}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:323;}}}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:327;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:325;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1053;}}}s:1:"e";a:3:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"v";a:1:{s:1:"e";a:3:{s:1:"M";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}}}}}s:1:"T";a:1:{s:1:"h";a:1:{s:1:"i";a:2:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}s:1:"n";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"y";a:1:{s:1:"T";a:1:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"d";a:2:{s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8811;}}}}}}}}}}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8810;}}}}}}}}}}}}}s:1:"w";a:1:{s:1:"L";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10;}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120081;}}}s:1:"o";a:4:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8288;}}}}}}s:1:"n";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"k";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:160;}}}}}}}}}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8469;}}}s:1:"t";a:11:{s:1:";";a:1:{s:9:"codepoint";i:10988;}s:1:"C";a:2:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8802;}}}}}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:"C";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8813;}}}}}}}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8742;}}}}}}}}}}}}}}}}}}s:1:"E";a:3:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8713;}}}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8800;}}}}}s:1:"x";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8708;}}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8815;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8817;}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8825;}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8821;}}}}}}}}}}}}}s:1:"L";a:1:{s:1:"e";a:2:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"T";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8938;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8940;}}}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8814;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8816;}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8824;}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8820;}}}}}}}}}}s:1:"P";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8832;}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8928;}}}}}}}}}}}}}}}}}}}s:1:"R";a:2:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"E";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8716;}}}}}}}}}}}}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"T";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8939;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8941;}}}}}}}}}}}}}}}}}}}s:1:"S";a:2:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"S";a:1:{s:1:"u";a:2:{s:1:"b";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8930;}}}}}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8931;}}}}}}}}}}}}}}}}}}}s:1:"u";a:3:{s:1:"b";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8840;}}}}}}}}}}s:1:"c";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8833;}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8929;}}}}}}}}}}}}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8841;}}}}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8769;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8772;}}}}}}s:1:"F";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8775;}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8777;}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8740;}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119977;}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:209;}s:9:"codepoint";i:209;}}}}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:925;}}}s:1:"O";a:14:{s:1:"E";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:338;}}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:211;}s:9:"codepoint";i:211;}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:212;}s:9:"codepoint";i:212;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1054;}}}s:1:"d";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:336;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120082;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:210;}s:9:"codepoint";i:210;}}}}}s:1:"m";a:3:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:332;}}}}s:1:"e";a:1:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:937;}}}}s:1:"i";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:927;}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120134;}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"C";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"l";a:1:{s:1:"y";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"Q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8220;}}}}}}}}}}}}s:1:"Q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8216;}}}}}}}}}}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10836;}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119978;}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:216;}s:9:"codepoint";i:216;}}}}}s:1:"t";a:1:{s:1:"i";a:2:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:213;}s:9:"codepoint";i:213;}}}s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10807;}}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:214;}s:9:"codepoint";i:214;}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"r";a:2:{s:1:"B";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:175;}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9182;}}s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9140;}}}}}}}}s:1:"P";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9180;}}}}}}}}}}}}}}}}s:1:"P";a:9:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8706;}}}}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1055;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120083;}}}s:1:"h";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:934;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:928;}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"M";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:177;}}}}}}}}}s:1:"o";a:2:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8460;}}}}}}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8473;}}}}s:1:"r";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10939;}s:1:"e";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8826;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10927;}}}}}}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8828;}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8830;}}}}}}}}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8243;}}}}s:1:"o";a:2:{s:1:"d";a:1:{s:1:"u";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8719;}}}}}s:1:"p";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8759;}s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8733;}}}}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119979;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:936;}}}}s:1:"Q";a:4:{s:1:"U";a:1:{s:1:"O";a:1:{s:1:"T";a:2:{s:1:";";a:1:{s:9:"codepoint";i:34;}s:9:"codepoint";i:34;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120084;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8474;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119980;}}}}}s:1:"R";a:12:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10512;}}}}}s:1:"E";a:1:{s:1:"G";a:2:{s:1:";";a:1:{s:9:"codepoint";i:174;}s:9:"codepoint";i:174;}}s:1:"a";a:3:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:340;}}}}}s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10219;}}}s:1:"r";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8608;}s:1:"t";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10518;}}}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:344;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:342;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1056;}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8476;}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:2:{s:1:"E";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8715;}}}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8651;}}}}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10607;}}}}}}}}}}}}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8476;}}}s:1:"h";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:929;}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:8:{s:1:"A";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10217;}}}}}}}}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8594;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8677;}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8644;}}}}}}}}}}}}}}}s:1:"C";a:1:{s:1:"e";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8969;}}}}}}}}s:1:"D";a:1:{s:1:"o";a:2:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10215;}}}}}}}}}}}}s:1:"w";a:1:{s:1:"n";a:2:{s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10589;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8642;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10581;}}}}}}}}}}}}}}s:1:"F";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8971;}}}}}}s:1:"T";a:2:{s:1:"e";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8866;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8614;}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10587;}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8883;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10704;}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8885;}}}}}}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:3:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10575;}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10588;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8638;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10580;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8640;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10579;}}}}}}}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8658;}}}}}}}}}}s:1:"o";a:2:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8477;}}}s:1:"u";a:1:{s:1:"n";a:1:{s:1:"d";a:1:{s:1:"I";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10608;}}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8667;}}}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8475;}}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8625;}}}s:1:"u";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"D";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"y";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10740;}}}}}}}}}}}}s:1:"S";a:13:{s:1:"H";a:2:{s:1:"C";a:1:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1065;}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1064;}}}}s:1:"O";a:1:{s:1:"F";a:1:{s:1:"T";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1068;}}}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:346;}}}}}}s:1:"c";a:5:{s:1:";";a:1:{s:9:"codepoint";i:10940;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:352;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:350;}}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:348;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1057;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120086;}}}s:1:"h";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"t";a:4:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8595;}}}}}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8592;}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8594;}}}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8593;}}}}}}}}}}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:931;}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"C";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8728;}}}}}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120138;}}}}s:1:"q";a:2:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8730;}}}s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:9633;}s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8851;}}}}}}}}}}}}}s:1:"S";a:1:{s:1:"u";a:2:{s:1:"b";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8847;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8849;}}}}}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8848;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8850;}}}}}}}}}}}}}}s:1:"U";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8852;}}}}}}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119982;}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8902;}}}}s:1:"u";a:4:{s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8912;}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8912;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8838;}}}}}}}}}}s:1:"c";a:2:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8827;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10928;}}}}}}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8829;}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8831;}}}}}}}}}}}s:1:"h";a:1:{s:1:"T";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8715;}}}}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8721;}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8913;}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8835;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8839;}}}}}}}}}}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8913;}}}}}}}s:1:"T";a:11:{s:1:"H";a:1:{s:1:"O";a:1:{s:1:"R";a:1:{s:1:"N";a:2:{s:1:";";a:1:{s:9:"codepoint";i:222;}s:9:"codepoint";i:222;}}}}s:1:"R";a:1:{s:1:"A";a:1:{s:1:"D";a:1:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8482;}}}}}s:1:"S";a:2:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1035;}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1062;}}}}s:1:"a";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:932;}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:356;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:354;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1058;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120087;}}}s:1:"h";a:2:{s:1:"e";a:2:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8756;}}}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:920;}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8201;}}}}}}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8764;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8771;}}}}}}s:1:"F";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8773;}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8776;}}}}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120139;}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8411;}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119983;}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:358;}}}}}}}s:1:"U";a:14:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:218;}s:9:"codepoint";i:218;}}}}s:1:"r";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8607;}s:1:"o";a:1:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10569;}}}}}}}}s:1:"b";a:1:{s:1:"r";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1038;}}}s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:364;}}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:219;}s:9:"codepoint";i:219;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1059;}}}s:1:"d";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:368;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120088;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:217;}s:9:"codepoint";i:217;}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:362;}}}}}s:1:"n";a:2:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"r";a:2:{s:1:"B";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:818;}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9183;}}s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9141;}}}}}}}}s:1:"P";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9181;}}}}}}}}}}}}}}}s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8899;}s:1:"P";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8846;}}}}}}}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:370;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120140;}}}}s:1:"p";a:8:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8593;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10514;}}}}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8645;}}}}}}}}}}}}}}}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8597;}}}}}}}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10606;}}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8869;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8613;}}}}}}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8657;}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8661;}}}}}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"r";a:2:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8598;}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8599;}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:978;}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:933;}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:366;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119984;}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:360;}}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:220;}s:9:"codepoint";i:220;}}}}s:1:"V";a:9:{s:1:"D";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8875;}}}}}s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10987;}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1042;}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8873;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10982;}}}}}}s:1:"e";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8897;}}s:1:"r";a:3:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8214;}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8214;}s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:4:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8739;}}}}s:1:"L";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:124;}}}}}s:1:"S";a:1:{s:1:"e";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10072;}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8768;}}}}}}}}}}}s:1:"y";a:1:{s:1:"T";a:1:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8202;}}}}}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120089;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120141;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119985;}}}}s:1:"v";a:1:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8874;}}}}}}}s:1:"W";a:5:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:372;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8896;}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120090;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120142;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119986;}}}}}s:1:"X";a:4:{s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120091;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:926;}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120143;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119987;}}}}}s:1:"Y";a:9:{s:1:"A";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1071;}}}}s:1:"I";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1031;}}}}s:1:"U";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1070;}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:221;}s:9:"codepoint";i:221;}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:374;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1067;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120092;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120144;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119988;}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:376;}}}}}s:1:"Z";a:8:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1046;}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:377;}}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:381;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1047;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:379;}}}}s:1:"e";a:2:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"W";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:918;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8488;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8484;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119989;}}}}}s:1:"a";a:16:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:225;}s:9:"codepoint";i:225;}}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:259;}}}}}}s:1:"c";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8766;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8767;}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:226;}s:9:"codepoint";i:226;}}}s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:180;}s:9:"codepoint";i:180;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1072;}}}s:1:"e";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:230;}s:9:"codepoint";i:230;}}}}s:1:"f";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8289;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120094;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:224;}s:9:"codepoint";i:224;}}}}}s:1:"l";a:2:{s:1:"e";a:2:{s:1:"f";a:1:{s:1:"s";a:1:{s:1:"y";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8501;}}}}}s:1:"p";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8501;}}}}s:1:"p";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:945;}}}}}s:1:"m";a:2:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:257;}}}s:1:"l";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10815;}}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:38;}s:9:"codepoint";i:38;}}s:1:"n";a:2:{s:1:"d";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8743;}s:1:"a";a:1:{s:1:"n";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10837;}}}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10844;}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10840;}}}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10842;}}}s:1:"g";a:7:{s:1:";";a:1:{s:9:"codepoint";i:8736;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10660;}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8736;}}}s:1:"m";a:1:{s:1:"s";a:1:{s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8737;}s:1:"a";a:8:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10664;}}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10665;}}s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10666;}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10667;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10668;}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10669;}}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10670;}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10671;}}}}}}s:1:"r";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8735;}s:1:"v";a:1:{s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8894;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10653;}}}}}}s:1:"s";a:2:{s:1:"p";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8738;}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8491;}}}s:1:"z";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9084;}}}}}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:261;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120146;}}}}s:1:"p";a:7:{s:1:";";a:1:{s:9:"codepoint";i:8776;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10864;}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10863;}}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8778;}}s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8779;}}}s:1:"o";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:39;}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8776;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8778;}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:229;}s:9:"codepoint";i:229;}}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119990;}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:42;}}s:1:"y";a:1:{s:1:"m";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8776;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8781;}}}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:227;}s:9:"codepoint";i:227;}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:228;}s:9:"codepoint";i:228;}}}s:1:"w";a:2:{s:1:"c";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8755;}}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10769;}}}}}}s:1:"b";a:16:{s:1:"N";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10989;}}}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"k";a:4:{s:1:"c";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8780;}}}}}s:1:"e";a:1:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1014;}}}}}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8245;}}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8765;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8909;}}}}}}}}s:1:"r";a:2:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8893;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8965;}s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8965;}}}}}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9141;}s:1:"t";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9142;}}}}}}}}s:1:"c";a:2:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8780;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1073;}}}s:1:"d";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8222;}}}}}s:1:"e";a:5:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"u";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8757;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8757;}}}}}}s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10672;}}}}}}s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1014;}}}}s:1:"r";a:1:{s:1:"n";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8492;}}}}}s:1:"t";a:3:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:946;}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8502;}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8812;}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120095;}}}s:1:"i";a:1:{s:1:"g";a:7:{s:1:"c";a:3:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8898;}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9711;}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8899;}}}}s:1:"o";a:3:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10752;}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10753;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10754;}}}}}}}s:1:"s";a:2:{s:1:"q";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10758;}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9733;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9661;}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9651;}}}}}}}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10756;}}}}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8897;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8896;}}}}}}}}s:1:"k";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10509;}}}}}}s:1:"l";a:3:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"k";a:3:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"z";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10731;}}}}}}}}s:1:"s";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9642;}}}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:9652;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9662;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9666;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9656;}}}}}}}}}}}}}}}}s:1:"n";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9251;}}}}s:1:"k";a:2:{i:1;a:2:{i:2;a:1:{s:1:";";a:1:{s:9:"codepoint";i:9618;}}i:4;a:1:{s:1:";";a:1:{s:9:"codepoint";i:9617;}}}i:3;a:1:{i:4;a:1:{s:1:";";a:1:{s:9:"codepoint";i:9619;}}}}s:1:"o";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9608;}}}}}s:1:"n";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8976;}}}}s:1:"o";a:4:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120147;}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8869;}s:1:"t";a:1:{s:1:"o";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8869;}}}}}s:1:"w";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8904;}}}}}s:1:"x";a:12:{s:1:"D";a:4:{s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9559;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9556;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9558;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9555;}}}s:1:"H";a:5:{s:1:";";a:1:{s:9:"codepoint";i:9552;}s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9574;}}s:1:"U";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9577;}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9572;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9575;}}}s:1:"U";a:4:{s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9565;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9562;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9564;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9561;}}}s:1:"V";a:7:{s:1:";";a:1:{s:9:"codepoint";i:9553;}s:1:"H";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9580;}}s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9571;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9568;}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9579;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9570;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9567;}}}s:1:"b";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10697;}}}}s:1:"d";a:4:{s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9557;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9554;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9488;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9484;}}}s:1:"h";a:5:{s:1:";";a:1:{s:9:"codepoint";i:9472;}s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9573;}}s:1:"U";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9576;}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9516;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9524;}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8863;}}}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8862;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8864;}}}}}}s:1:"u";a:4:{s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9563;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9560;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9496;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9492;}}}s:1:"v";a:7:{s:1:";";a:1:{s:9:"codepoint";i:9474;}s:1:"H";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9578;}}s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9569;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9566;}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9532;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9508;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9500;}}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8245;}}}}}}s:1:"r";a:2:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:728;}}}}s:1:"v";a:1:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:166;}s:9:"codepoint";i:166;}}}}}s:1:"s";a:4:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119991;}}}s:1:"e";a:1:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8271;}}}}s:1:"i";a:1:{s:1:"m";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8765;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8909;}}}}s:1:"o";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:92;}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10693;}}}}}s:1:"u";a:2:{s:1:"l";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8226;}s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8226;}}}}}s:1:"m";a:1:{s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8782;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10926;}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8783;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8783;}}}}}}}s:1:"c";a:15:{s:1:"a";a:3:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:263;}}}}}s:1:"p";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8745;}s:1:"a";a:1:{s:1:"n";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10820;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10825;}}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10827;}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10823;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10816;}}}}}s:1:"r";a:2:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8257;}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:711;}}}}}s:1:"c";a:4:{s:1:"a";a:2:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10829;}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:269;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:231;}s:9:"codepoint";i:231;}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:265;}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10828;}s:1:"s";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10832;}}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:267;}}}}s:1:"e";a:3:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:184;}s:9:"codepoint";i:184;}}}s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10674;}}}}}}s:1:"n";a:1:{s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:162;}s:9:"codepoint";i:162;s:1:"e";a:1:{s:1:"r";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:183;}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120096;}}}s:1:"h";a:3:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1095;}}}s:1:"e";a:1:{s:1:"c";a:1:{s:1:"k";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10003;}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10003;}}}}}}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:967;}}}s:1:"i";a:1:{s:1:"r";a:7:{s:1:";";a:1:{s:9:"codepoint";i:9675;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10691;}}s:1:"c";a:3:{s:1:";";a:1:{s:9:"codepoint";i:710;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8791;}}}s:1:"l";a:1:{s:1:"e";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8634;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8635;}}}}}}}}}}}s:1:"d";a:5:{s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:174;}}s:1:"S";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9416;}}s:1:"a";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8859;}}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8858;}}}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8861;}}}}}}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8791;}}s:1:"f";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10768;}}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10991;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10690;}}}}}}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9827;}s:1:"u";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9827;}}}}}}}}s:1:"o";a:4:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:58;}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8788;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8788;}}}}}}s:1:"m";a:2:{s:1:"m";a:1:{s:1:"a";a:2:{s:1:";";a:1:{s:9:"codepoint";i:44;}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64;}}}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8705;}s:1:"f";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8728;}}}s:1:"l";a:1:{s:1:"e";a:2:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8705;}}}}}s:1:"x";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8450;}}}}}}}}s:1:"n";a:2:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8773;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10861;}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8750;}}}}}s:1:"p";a:3:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120148;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8720;}}}}s:1:"y";a:3:{s:1:";";a:1:{s:9:"codepoint";i:169;}s:9:"codepoint";i:169;s:1:"s";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8471;}}}}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8629;}}}}s:1:"o";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10007;}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119992;}}}s:1:"u";a:2:{s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10959;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10961;}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10960;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10962;}}}}}s:1:"t";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8943;}}}}}s:1:"u";a:7:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:2:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10552;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10549;}}}}}}s:1:"e";a:2:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8926;}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8927;}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8630;}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10557;}}}}}}s:1:"p";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8746;}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10824;}}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10822;}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10826;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8845;}}}}s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10821;}}}}s:1:"r";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8631;}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10556;}}}}}s:1:"l";a:1:{s:1:"y";a:3:{s:1:"e";a:1:{s:1:"q";a:2:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8926;}}}}}s:1:"s";a:1:{s:1:"u";a:1:{s:1:"c";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8927;}}}}}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8910;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8911;}}}}}}}}s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:164;}s:9:"codepoint";i:164;}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8630;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8631;}}}}}}}}}}}}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8910;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8911;}}}}}s:1:"w";a:2:{s:1:"c";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8754;}}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8753;}}}}}s:1:"y";a:1:{s:1:"l";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9005;}}}}}}}s:1:"d";a:19:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8659;}}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10597;}}}}s:1:"a";a:4:{s:1:"g";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8224;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8504;}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8595;}}}s:1:"s";a:1:{s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8208;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8867;}}}}}s:1:"b";a:2:{s:1:"k";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10511;}}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:733;}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:271;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1076;}}}s:1:"d";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8518;}s:1:"a";a:2:{s:1:"g";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8225;}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8650;}}}}s:1:"o";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10871;}}}}}}}s:1:"e";a:3:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:176;}s:9:"codepoint";i:176;}s:1:"l";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:948;}}}}s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10673;}}}}}}}s:1:"f";a:2:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10623;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120097;}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8643;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8642;}}}}}s:1:"i";a:5:{s:1:"a";a:1:{s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8900;}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8900;}s:1:"s";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9830;}}}}}}}}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9830;}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:168;}}s:1:"g";a:1:{s:1:"a";a:1:{s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:989;}}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8946;}}}}s:1:"v";a:3:{s:1:";";a:1:{s:9:"codepoint";i:247;}s:1:"i";a:1:{s:1:"d";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:247;}s:9:"codepoint";i:247;s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8903;}}}}}}}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8903;}}}}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1106;}}}}s:1:"l";a:1:{s:1:"c";a:2:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8990;}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8973;}}}}}}s:1:"o";a:5:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:36;}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120149;}}}s:1:"t";a:5:{s:1:";";a:1:{s:9:"codepoint";i:729;}s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8784;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8785;}}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8760;}}}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8724;}}}}}s:1:"s";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8865;}}}}}}}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8966;}}}}}}}}}}}}}s:1:"w";a:1:{s:1:"n";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8595;}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8650;}}}}}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8643;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8642;}}}}}}}}}}}}}}}}s:1:"r";a:2:{s:1:"b";a:1:{s:1:"k";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10512;}}}}}}}s:1:"c";a:2:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8991;}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8972;}}}}}}s:1:"s";a:3:{s:1:"c";a:2:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119993;}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1109;}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10742;}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:273;}}}}}}s:1:"t";a:2:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8945;}}}}s:1:"r";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9663;}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9662;}}}}}s:1:"u";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8693;}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10607;}}}}}s:1:"w";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10662;}}}}}}}s:1:"z";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1119;}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10239;}}}}}}}}}s:1:"e";a:18:{s:1:"D";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10871;}}}}s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8785;}}}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:233;}s:9:"codepoint";i:233;}}}}s:1:"s";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10862;}}}}}}s:1:"c";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:283;}}}}}s:1:"i";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8790;}s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:234;}s:9:"codepoint";i:234;}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8789;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1101;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:279;}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8519;}}s:1:"f";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8786;}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120098;}}}s:1:"g";a:3:{s:1:";";a:1:{s:9:"codepoint";i:10906;}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:232;}s:9:"codepoint";i:232;}}}}s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10902;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10904;}}}}}}s:1:"l";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10905;}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9191;}}}}}}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8467;}}s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10901;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10903;}}}}}}s:1:"m";a:3:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:275;}}}}s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8709;}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8709;}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8709;}}}}}s:1:"s";a:1:{s:1:"p";a:2:{i:1;a:2:{i:3;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8196;}}i:4;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8197;}}}s:1:";";a:1:{s:9:"codepoint";i:8195;}}}}s:1:"n";a:2:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:331;}}s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8194;}}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:281;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120150;}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8917;}s:1:"s";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10723;}}}}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10865;}}}}s:1:"s";a:1:{s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:1013;}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:949;}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:949;}}}}}s:1:"q";a:4:{s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8790;}}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8789;}}}}}}s:1:"s";a:2:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8770;}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:2:{s:1:"g";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10902;}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10901;}}}}}}}}}}s:1:"u";a:3:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:61;}}}}s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8799;}}}}s:1:"i";a:1:{s:1:"v";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8801;}s:1:"D";a:1:{s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10872;}}}}}}s:1:"v";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10725;}}}}}}}}s:1:"r";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8787;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10609;}}}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8495;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8784;}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8770;}}}}s:1:"t";a:2:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:951;}}s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:240;}s:9:"codepoint";i:240;}}s:1:"u";a:2:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:235;}s:9:"codepoint";i:235;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8364;}}}}s:1:"x";a:3:{s:1:"c";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:33;}}}s:1:"i";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8707;}}}}s:1:"p";a:2:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8496;}}}}}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8519;}}}}}}}}}}}}}s:1:"f";a:11:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8786;}}}}}}}}}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1092;}}}s:1:"e";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9792;}}}}}}s:1:"f";a:3:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64259;}}}}}s:1:"l";a:2:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64256;}}}s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64260;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120099;}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64257;}}}}}s:1:"l";a:3:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9837;}}}s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64258;}}}}s:1:"t";a:1:{s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9649;}}}}}s:1:"n";a:1:{s:1:"o";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:402;}}}}s:1:"o";a:2:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120151;}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8704;}}}}s:1:"k";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8916;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10969;}}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10765;}}}}}}}}s:1:"r";a:2:{s:1:"a";a:2:{s:1:"c";a:6:{i:1;a:6:{i:2;a:2:{s:1:";";a:1:{s:9:"codepoint";i:189;}s:9:"codepoint";i:189;}i:3;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8531;}}i:4;a:2:{s:1:";";a:1:{s:9:"codepoint";i:188;}s:9:"codepoint";i:188;}i:5;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8533;}}i:6;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8537;}}i:8;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8539;}}}i:2;a:2:{i:3;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8532;}}i:5;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8534;}}}i:3;a:3:{i:4;a:2:{s:1:";";a:1:{s:9:"codepoint";i:190;}s:9:"codepoint";i:190;}i:5;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8535;}}i:8;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8540;}}}i:4;a:1:{i:5;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8536;}}}i:5;a:2:{i:6;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8538;}}i:8;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8541;}}}i:7;a:1:{i:8;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8542;}}}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8260;}}}}s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8994;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119995;}}}}}s:1:"g";a:16:{s:1:"E";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8807;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10892;}}}s:1:"a";a:3:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:501;}}}}}s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:2:{s:1:";";a:1:{s:9:"codepoint";i:947;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:989;}}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10886;}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:287;}}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:285;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1075;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:289;}}}}s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8805;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8923;}}s:1:"q";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8805;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8807;}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10878;}}}}}}}s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10878;}s:1:"c";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10921;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10880;}s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10882;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10884;}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10900;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120100;}}}s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8811;}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8921;}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8503;}}}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1107;}}}}s:1:"l";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8823;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10898;}}s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10917;}}s:1:"j";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10916;}}}s:1:"n";a:4:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8809;}}s:1:"a";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10890;}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10890;}}}}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10888;}s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10888;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8809;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8935;}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120152;}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:96;}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8458;}}}s:1:"i";a:1:{s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8819;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10894;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10896;}}}}}s:1:"t";a:7:{s:1:";";a:1:{s:9:"codepoint";i:62;}s:9:"codepoint";i:62;s:1:"c";a:2:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10919;}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10874;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8919;}}}}s:1:"l";a:1:{s:1:"P";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10645;}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10876;}}}}}}s:1:"r";a:5:{s:1:"a";a:2:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10886;}}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10616;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8919;}}}}s:1:"e";a:1:{s:1:"q";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8923;}}}}}s:1:"q";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10892;}}}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8823;}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8819;}}}}}}}s:1:"h";a:10:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8660;}}}}s:1:"a";a:4:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8202;}}}}}s:1:"l";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:189;}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8459;}}}}}s:1:"r";a:2:{s:1:"d";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1098;}}}}s:1:"r";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8596;}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10568;}}}}s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8621;}}}}}s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8463;}}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:293;}}}}}s:1:"e";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9829;}s:1:"u";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9829;}}}}}}}}s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8230;}}}}}s:1:"r";a:1:{s:1:"c";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8889;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120101;}}}s:1:"k";a:1:{s:1:"s";a:2:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10533;}}}}}}s:1:"w";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10534;}}}}}}}}s:1:"o";a:5:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8703;}}}}s:1:"m";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8763;}}}}}s:1:"o";a:1:{s:1:"k";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8617;}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8618;}}}}}}}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120153;}}}s:1:"r";a:1:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8213;}}}}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119997;}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8463;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:295;}}}}}}s:1:"y";a:2:{s:1:"b";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8259;}}}}}s:1:"p";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8208;}}}}}}}s:1:"i";a:15:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:237;}s:9:"codepoint";i:237;}}}}}s:1:"c";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8291;}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:238;}s:9:"codepoint";i:238;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1080;}}}s:1:"e";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1077;}}}s:1:"x";a:1:{s:1:"c";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:161;}s:9:"codepoint";i:161;}}}}s:1:"f";a:2:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8660;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120102;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:236;}s:9:"codepoint";i:236;}}}}}s:1:"i";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8520;}s:1:"i";a:2:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10764;}}}}s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8749;}}}}s:1:"n";a:1:{s:1:"f";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10716;}}}}}s:1:"o";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8489;}}}}}s:1:"j";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:307;}}}}}s:1:"m";a:3:{s:1:"a";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:299;}}}s:1:"g";a:3:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8465;}}s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8464;}}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8465;}}}}}}s:1:"t";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:305;}}}}s:1:"o";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8887;}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:437;}}}}}s:1:"n";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8712;}s:1:"c";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8453;}}}}}s:1:"f";a:1:{s:1:"i";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8734;}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10717;}}}}}}}s:1:"o";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:305;}}}}}s:1:"t";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8747;}s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8890;}}}}s:1:"e";a:2:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8484;}}}}}s:1:"r";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8890;}}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10775;}}}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10812;}}}}}}}s:1:"o";a:4:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1105;}}}s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:303;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120154;}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:953;}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10812;}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:191;}s:9:"codepoint";i:191;}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119998;}}}s:1:"i";a:1:{s:1:"n";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8712;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8953;}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8949;}}}}s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8948;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8947;}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8712;}}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8290;}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:297;}}}}}}s:1:"u";a:2:{s:1:"k";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1110;}}}}s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:239;}s:9:"codepoint";i:239;}}}}s:1:"j";a:6:{s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:309;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1081;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120103;}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:567;}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120155;}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119999;}}}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1112;}}}}}}s:1:"u";a:1:{s:1:"k";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1108;}}}}}}s:1:"k";a:8:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"a";a:2:{s:1:";";a:1:{s:9:"codepoint";i:954;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1008;}}}}}}s:1:"c";a:2:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:311;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1082;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120104;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:312;}}}}}}s:1:"h";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1093;}}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1116;}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120156;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120000;}}}}}s:1:"l";a:22:{s:1:"A";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8666;}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8656;}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10523;}}}}}}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10510;}}}}}s:1:"E";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8806;}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10891;}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10594;}}}}s:1:"a";a:9:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:314;}}}}}s:1:"e";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10676;}}}}}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8466;}}}}}s:1:"m";a:1:{s:1:"b";a:1:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:955;}}}}}s:1:"n";a:1:{s:1:"g";a:3:{s:1:";";a:1:{s:9:"codepoint";i:10216;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10641;}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10216;}}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10885;}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:171;}s:9:"codepoint";i:171;}}}s:1:"r";a:1:{s:1:"r";a:8:{s:1:";";a:1:{s:9:"codepoint";i:8592;}s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8676;}s:1:"f";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10527;}}}}s:1:"f";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10525;}}}s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8617;}}}s:1:"l";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8619;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10553;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10611;}}}}s:1:"t";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8610;}}}}}s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:10923;}s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10521;}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10925;}}}}s:1:"b";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10508;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10098;}}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:123;}}s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:91;}}}}s:1:"k";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10635;}}s:1:"s";a:1:{s:1:"l";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10639;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10637;}}}}}}}s:1:"c";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:318;}}}}}s:1:"e";a:2:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:316;}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8968;}}}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:123;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1083;}}}s:1:"d";a:4:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10550;}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8220;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8222;}}}}}s:1:"r";a:2:{s:1:"d";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10599;}}}}}s:1:"u";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10571;}}}}}}}s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8626;}}}}s:1:"e";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8804;}s:1:"f";a:1:{s:1:"t";a:5:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8592;}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8610;}}}}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8637;}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8636;}}}}}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8647;}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8596;}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8646;}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8651;}}}}}}}}}s:1:"s";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8621;}}}}}}}}}}}}}}}}s:1:"t";a:1:{s:1:"h";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8907;}}}}}}}}}}}}}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8922;}}s:1:"q";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8804;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8806;}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10877;}}}}}}}s:1:"s";a:5:{s:1:";";a:1:{s:9:"codepoint";i:10877;}s:1:"c";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10920;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10879;}s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10881;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10883;}}}}}}s:1:"g";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10899;}}}}s:1:"s";a:5:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10885;}}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8918;}}}}s:1:"e";a:1:{s:1:"q";a:2:{s:1:"g";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8922;}}}}s:1:"q";a:1:{s:1:"g";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10891;}}}}}}}s:1:"g";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8822;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8818;}}}}}}}s:1:"f";a:3:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10620;}}}}}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8970;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120105;}}}s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8822;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10897;}}}s:1:"h";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8637;}}s:1:"u";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8636;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10602;}}}}}s:1:"b";a:1:{s:1:"l";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9604;}}}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1113;}}}}s:1:"l";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8810;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8647;}}}}s:1:"c";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8990;}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10603;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9722;}}}}}s:1:"m";a:2:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:320;}}}}}s:1:"o";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9136;}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9136;}}}}}}}}}}s:1:"n";a:4:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8808;}}s:1:"a";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10889;}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10889;}}}}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10887;}s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10887;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8808;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8934;}}}}}s:1:"o";a:8:{s:1:"a";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10220;}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8701;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10214;}}}}s:1:"n";a:1:{s:1:"g";a:3:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10229;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10231;}}}}}}}}}}}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10236;}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10230;}}}}}}}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8619;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8620;}}}}}}}}}}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10629;}}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120157;}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10797;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10804;}}}}}}s:1:"w";a:2:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8727;}}}}s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:95;}}}}}s:1:"z";a:3:{s:1:";";a:1:{s:9:"codepoint";i:9674;}s:1:"e";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9674;}}}}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10731;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:40;}s:1:"l";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10643;}}}}}}s:1:"r";a:5:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8646;}}}}s:1:"c";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8991;}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8651;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10605;}}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8206;}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8895;}}}}}s:1:"s";a:6:{s:1:"a";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8249;}}}}}s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120001;}}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8624;}}s:1:"i";a:1:{s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8818;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10893;}}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10895;}}}}s:1:"q";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:91;}}s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8216;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8218;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:322;}}}}}}s:1:"t";a:9:{s:1:";";a:1:{s:9:"codepoint";i:60;}s:9:"codepoint";i:60;s:1:"c";a:2:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10918;}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10873;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8918;}}}}s:1:"h";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8907;}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8905;}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10614;}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10875;}}}}}}s:1:"r";a:2:{s:1:"P";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10646;}}}}s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:9667;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8884;}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9666;}}}}}s:1:"u";a:1:{s:1:"r";a:2:{s:1:"d";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10570;}}}}}}s:1:"u";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10598;}}}}}}}}s:1:"m";a:14:{s:1:"D";a:1:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8762;}}}}}s:1:"a";a:4:{s:1:"c";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:175;}s:9:"codepoint";i:175;}}s:1:"l";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9794;}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10016;}s:1:"e";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10016;}}}}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8614;}s:1:"s";a:1:{s:1:"t";a:1:{s:1:"o";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8614;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8615;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8612;}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8613;}}}}}}}s:1:"r";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9646;}}}}}}s:1:"c";a:2:{s:1:"o";a:1:{s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10793;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1084;}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8212;}}}}}s:1:"e";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8737;}}}}}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120106;}}}s:1:"h";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8487;}}}s:1:"i";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:181;}s:9:"codepoint";i:181;}}}s:1:"d";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8739;}s:1:"a";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:42;}}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10992;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:183;}s:9:"codepoint";i:183;}}}}s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8722;}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8863;}}s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8760;}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10794;}}}}}}}s:1:"l";a:2:{s:1:"c";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10971;}}}s:1:"d";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8230;}}}}s:1:"n";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8723;}}}}}}s:1:"o";a:2:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8871;}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120158;}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8723;}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120002;}}}s:1:"t";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8766;}}}}}}s:1:"u";a:3:{s:1:";";a:1:{s:9:"codepoint";i:956;}s:1:"l";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8888;}}}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8888;}}}}}}s:1:"n";a:23:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8653;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8654;}}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8655;}}}}}}}}}}}s:1:"V";a:2:{s:1:"D";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8879;}}}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8878;}}}}}}s:1:"a";a:4:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8711;}}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:324;}}}}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8777;}s:1:"o";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:329;}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8777;}}}}}}s:1:"t";a:1:{s:1:"u";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9838;}s:1:"a";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9838;}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8469;}}}}}}}}s:1:"b";a:1:{s:1:"s";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:160;}s:9:"codepoint";i:160;}}}s:1:"c";a:5:{s:1:"a";a:2:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10819;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:328;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:326;}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8775;}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10818;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1085;}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8211;}}}}}s:1:"e";a:6:{s:1:";";a:1:{s:9:"codepoint";i:8800;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8663;}}}}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10532;}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8599;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8599;}}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8802;}}}}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10536;}}}}}s:1:"x";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8708;}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8708;}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120107;}}}s:1:"g";a:3:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8817;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8817;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8821;}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8815;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8815;}}}}s:1:"h";a:3:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8654;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8622;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10994;}}}}}s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8715;}s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8956;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8954;}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8715;}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1114;}}}}s:1:"l";a:6:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8653;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8602;}}}}s:1:"d";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8229;}}}s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8816;}s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8602;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8622;}}}}}}}}}}}}}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8816;}}s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8814;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8820;}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8814;}s:1:"r";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8938;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8940;}}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8740;}}}}s:1:"o";a:2:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120159;}}}s:1:"t";a:4:{s:1:";";a:1:{s:9:"codepoint";i:172;}s:9:"codepoint";i:172;s:1:"i";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8713;}s:1:"v";a:3:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8713;}}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8951;}}s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8950;}}}}}s:1:"n";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8716;}s:1:"v";a:3:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8716;}}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8958;}}s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8957;}}}}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8742;}s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8742;}}}}}}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10772;}}}}}}s:1:"r";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8832;}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8928;}}}}s:1:"e";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8832;}}}}}s:1:"r";a:4:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8655;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8603;}}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8603;}}}}}}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8939;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8941;}}}}}}s:1:"s";a:7:{s:1:"c";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8833;}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8929;}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120003;}}}s:1:"h";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"t";a:2:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8740;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8742;}}}}}}}}}}}}}s:1:"i";a:1:{s:1:"m";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8769;}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8772;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8772;}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8740;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8742;}}}}s:1:"q";a:1:{s:1:"s";a:1:{s:1:"u";a:2:{s:1:"b";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8930;}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8931;}}}}}}s:1:"u";a:3:{s:1:"b";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8836;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8840;}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8840;}}}}}}}s:1:"c";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8833;}}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8837;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8841;}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8841;}}}}}}}}}s:1:"t";a:4:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8825;}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:241;}s:9:"codepoint";i:241;}}}}s:1:"l";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8824;}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8938;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8940;}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8939;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8941;}}}}}}}}}}}}}}}}s:1:"u";a:2:{s:1:";";a:1:{s:9:"codepoint";i:957;}s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:35;}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8470;}}}}s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8199;}}}}}s:1:"v";a:6:{s:1:"D";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8877;}}}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10500;}}}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8876;}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"f";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10718;}}}}}}s:1:"l";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10498;}}}}}s:1:"r";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10499;}}}}}}s:1:"w";a:3:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8662;}}}}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10531;}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8598;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8598;}}}}}}s:1:"n";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10535;}}}}}}}s:1:"o";a:18:{s:1:"S";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9416;}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:243;}s:9:"codepoint";i:243;}}}}s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8859;}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8858;}s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:244;}s:9:"codepoint";i:244;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1086;}}}s:1:"d";a:5:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8861;}}}}s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:337;}}}}}s:1:"i";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10808;}}}s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8857;}}}s:1:"s";a:1:{s:1:"o";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10684;}}}}}}s:1:"e";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:339;}}}}}s:1:"f";a:2:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10687;}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120108;}}}s:1:"g";a:3:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:731;}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:242;}s:9:"codepoint";i:242;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10689;}}}s:1:"h";a:2:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10677;}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8486;}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8750;}}}}s:1:"l";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8634;}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10686;}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10683;}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8254;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10688;}}}s:1:"m";a:3:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:333;}}}}s:1:"e";a:1:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:969;}}}}s:1:"i";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:959;}}}}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10678;}}s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8854;}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120160;}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10679;}}}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10681;}}}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8853;}}}}}s:1:"r";a:7:{s:1:";";a:1:{s:9:"codepoint";i:8744;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8635;}}}}s:1:"d";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10845;}s:1:"e";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8500;}s:1:"o";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8500;}}}}}s:1:"f";a:2:{s:1:";";a:1:{s:9:"codepoint";i:170;}s:9:"codepoint";i:170;}s:1:"m";a:2:{s:1:";";a:1:{s:9:"codepoint";i:186;}s:9:"codepoint";i:186;}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8886;}}}}}s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10838;}}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10839;}}}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10843;}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8500;}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:248;}s:9:"codepoint";i:248;}}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8856;}}}}s:1:"t";a:1:{s:1:"i";a:2:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:245;}s:9:"codepoint";i:245;}}}s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8855;}s:1:"a";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10806;}}}}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:246;}s:9:"codepoint";i:246;}}}s:1:"v";a:1:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9021;}}}}}}s:1:"p";a:12:{s:1:"a";a:1:{s:1:"r";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8741;}s:1:"a";a:3:{s:1:";";a:1:{s:9:"codepoint";i:182;}s:9:"codepoint";i:182;s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8741;}}}}}}s:1:"s";a:2:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10995;}}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:11005;}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8706;}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1087;}}}s:1:"e";a:1:{s:1:"r";a:5:{s:1:"c";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:37;}}}}s:1:"i";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:46;}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8240;}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8869;}}s:1:"t";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8241;}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120109;}}}s:1:"h";a:3:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:966;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:966;}}}s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8499;}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9742;}}}}}s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:960;}s:1:"t";a:1:{s:1:"c";a:1:{s:1:"h";a:1:{s:1:"f";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8916;}}}}}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:982;}}}s:1:"l";a:2:{s:1:"a";a:1:{s:1:"n";a:2:{s:1:"c";a:1:{s:1:"k";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8463;}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8462;}}}}s:1:"k";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8463;}}}}}s:1:"u";a:1:{s:1:"s";a:9:{s:1:";";a:1:{s:9:"codepoint";i:43;}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10787;}}}}}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8862;}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10786;}}}}s:1:"d";a:2:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8724;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10789;}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10866;}}s:1:"m";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:177;}s:9:"codepoint";i:177;}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10790;}}}}s:1:"t";a:1:{s:1:"w";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10791;}}}}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:177;}}s:1:"o";a:3:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10773;}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120161;}}}s:1:"u";a:1:{s:1:"n";a:1:{s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:163;}s:9:"codepoint";i:163;}}}}s:1:"r";a:10:{s:1:";";a:1:{s:9:"codepoint";i:8826;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10931;}}s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10935;}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8828;}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10927;}s:1:"c";a:6:{s:1:";";a:1:{s:9:"codepoint";i:8826;}s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10935;}}}}}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"l";a:1:{s:1:"y";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8828;}}}}}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10927;}}}s:1:"n";a:3:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10937;}}}}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10933;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8936;}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8830;}}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8242;}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8473;}}}}}s:1:"n";a:3:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10933;}}s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10937;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8936;}}}}}s:1:"o";a:3:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8719;}}s:1:"f";a:3:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9006;}}}}}s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8978;}}}}}s:1:"s";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8979;}}}}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8733;}s:1:"t";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8733;}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8830;}}}}s:1:"u";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8880;}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120005;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:968;}}}s:1:"u";a:1:{s:1:"n";a:1:{s:1:"c";a:1:{s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8200;}}}}}}}s:1:"q";a:6:{s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120110;}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10764;}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120162;}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8279;}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120006;}}}}s:1:"u";a:3:{s:1:"a";a:1:{s:1:"t";a:2:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8461;}}}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10774;}}}}}}s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:63;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8799;}}}}}}s:1:"o";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:34;}s:9:"codepoint";i:34;}}}}s:1:"r";a:21:{s:1:"A";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8667;}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8658;}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10524;}}}}}}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10511;}}}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10596;}}}}s:1:"a";a:7:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10714;}}s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:341;}}}}}s:1:"d";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8730;}}}}s:1:"e";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10675;}}}}}}}s:1:"n";a:1:{s:1:"g";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10217;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10642;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10661;}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10217;}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:187;}s:9:"codepoint";i:187;}}}s:1:"r";a:1:{s:1:"r";a:11:{s:1:";";a:1:{s:9:"codepoint";i:8594;}s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10613;}}}s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8677;}s:1:"f";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10528;}}}}s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10547;}}s:1:"f";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10526;}}}s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8618;}}}s:1:"l";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8620;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10565;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10612;}}}}s:1:"t";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8611;}}}s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8605;}}}}s:1:"t";a:2:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10522;}}}}s:1:"i";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8758;}s:1:"n";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8474;}}}}}}}}}s:1:"b";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10509;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10099;}}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:125;}}s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:93;}}}}s:1:"k";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10636;}}s:1:"s";a:1:{s:1:"l";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10638;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10640;}}}}}}}s:1:"c";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:345;}}}}}s:1:"e";a:2:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:343;}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8969;}}}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:125;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1088;}}}s:1:"d";a:4:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10551;}}}s:1:"l";a:1:{s:1:"d";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10601;}}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8221;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8221;}}}}}s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8627;}}}}s:1:"e";a:3:{s:1:"a";a:1:{s:1:"l";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8476;}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8475;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8476;}}}}}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8477;}}}}s:1:"c";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9645;}}}s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:174;}s:9:"codepoint";i:174;}}s:1:"f";a:3:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10621;}}}}}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8971;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120111;}}}s:1:"h";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8641;}}s:1:"u";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8640;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10604;}}}}}s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:961;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1009;}}}}s:1:"i";a:3:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:6:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8594;}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8611;}}}}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8641;}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8640;}}}}}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8644;}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8652;}}}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8649;}}}}}}}}}}}}s:1:"s";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8605;}}}}}}}}}}}s:1:"t";a:1:{s:1:"h";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8908;}}}}}}}}}}}}}}s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:730;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8787;}}}}}}}}}}}}s:1:"l";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8644;}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8652;}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8207;}}}s:1:"m";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9137;}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9137;}}}}}}}}}}s:1:"n";a:1:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10990;}}}}}s:1:"o";a:4:{s:1:"a";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10221;}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8702;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10215;}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10630;}}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120163;}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10798;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10805;}}}}}}}s:1:"p";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:41;}s:1:"g";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10644;}}}}}s:1:"p";a:1:{s:1:"o";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10770;}}}}}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8649;}}}}}s:1:"s";a:4:{s:1:"a";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8250;}}}}}s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120007;}}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8625;}}s:1:"q";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:93;}}s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8217;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8217;}}}}}}s:1:"t";a:3:{s:1:"h";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8908;}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8906;}}}}}s:1:"r";a:1:{s:1:"i";a:4:{s:1:";";a:1:{s:9:"codepoint";i:9657;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8885;}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9656;}}s:1:"l";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10702;}}}}}}}}s:1:"u";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10600;}}}}}}}s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8478;}}}s:1:"s";a:19:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:347;}}}}}}s:1:"b";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8218;}}}}}s:1:"c";a:10:{s:1:";";a:1:{s:9:"codepoint";i:8827;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10932;}}s:1:"a";a:2:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10936;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:353;}}}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8829;}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10928;}s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:351;}}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:349;}}}}s:1:"n";a:3:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10934;}}s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10938;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8937;}}}}}s:1:"p";a:1:{s:1:"o";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10771;}}}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8831;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1089;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8901;}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8865;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10854;}}}}}s:1:"e";a:7:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8664;}}}}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10533;}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8600;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8600;}}}}}}s:1:"c";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:167;}s:9:"codepoint";i:167;}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:59;}}}s:1:"s";a:1:{s:1:"w";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10537;}}}}}s:1:"t";a:1:{s:1:"m";a:2:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}}s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}s:1:"x";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10038;}}}}s:1:"f";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:120112;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8994;}}}}}}s:1:"h";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9839;}}}}s:1:"c";a:2:{s:1:"h";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1097;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1096;}}}s:1:"o";a:1:{s:1:"r";a:1:{s:1:"t";a:2:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8739;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8741;}}}}}}}}}}}}s:1:"y";a:2:{s:1:";";a:1:{s:9:"codepoint";i:173;}s:9:"codepoint";i:173;}}s:1:"i";a:2:{s:1:"g";a:1:{s:1:"m";a:1:{s:1:"a";a:3:{s:1:";";a:1:{s:9:"codepoint";i:963;}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:962;}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:962;}}}}}s:1:"m";a:8:{s:1:";";a:1:{s:9:"codepoint";i:8764;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10858;}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8771;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8771;}}}s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10910;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10912;}}}s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10909;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10911;}}}s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8774;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10788;}}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10610;}}}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8592;}}}}}s:1:"m";a:4:{s:1:"a";a:2:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}}}}}}}}s:1:"s";a:1:{s:1:"h";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10803;}}}}}s:1:"e";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10724;}}}}}}}s:1:"i";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8739;}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8995;}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10922;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10924;}}}}s:1:"o";a:3:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1100;}}}}}s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:47;}s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10692;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9023;}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120164;}}}}s:1:"p";a:1:{s:1:"a";a:2:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9824;}s:1:"u";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9824;}}}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8741;}}}}s:1:"q";a:3:{s:1:"c";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8851;}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8852;}}}}s:1:"s";a:1:{s:1:"u";a:2:{s:1:"b";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8847;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8849;}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8847;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8849;}}}}}}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8848;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8850;}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8848;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8850;}}}}}}}}}s:1:"u";a:3:{s:1:";";a:1:{s:9:"codepoint";i:9633;}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9633;}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9642;}}}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9642;}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8594;}}}}}s:1:"s";a:4:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120008;}}}s:1:"e";a:1:{s:1:"t";a:1:{s:1:"m";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8995;}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8902;}}}}}}s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9734;}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9733;}}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:"e";a:1:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1013;}}}}}}}}s:1:"p";a:1:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:981;}}}}}}}}}s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:175;}}}}}s:1:"u";a:5:{s:1:"b";a:9:{s:1:";";a:1:{s:9:"codepoint";i:8834;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10949;}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10941;}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8838;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10947;}}}}}s:1:"m";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10945;}}}}}s:1:"n";a:2:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10955;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8842;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10943;}}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10617;}}}}}s:1:"s";a:3:{s:1:"e";a:1:{s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8834;}s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8838;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10949;}}}}s:1:"n";a:1:{s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8842;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10955;}}}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10951;}}}s:1:"u";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10965;}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10963;}}}}}s:1:"c";a:1:{s:1:"c";a:6:{s:1:";";a:1:{s:9:"codepoint";i:8827;}s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10936;}}}}}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"l";a:1:{s:1:"y";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8829;}}}}}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10928;}}}s:1:"n";a:3:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10938;}}}}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10934;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8937;}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8831;}}}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8721;}}s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9834;}}}s:1:"p";a:13:{i:1;a:2:{s:1:";";a:1:{s:9:"codepoint";i:185;}s:9:"codepoint";i:185;}i:2;a:2:{s:1:";";a:1:{s:9:"codepoint";i:178;}s:9:"codepoint";i:178;}i:3;a:2:{s:1:";";a:1:{s:9:"codepoint";i:179;}s:9:"codepoint";i:179;}s:1:";";a:1:{s:9:"codepoint";i:8835;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10950;}}s:1:"d";a:2:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10942;}}}s:1:"s";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10968;}}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8839;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10948;}}}}}s:1:"h";a:1:{s:1:"s";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10967;}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10619;}}}}}s:1:"m";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10946;}}}}}s:1:"n";a:2:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10956;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8843;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10944;}}}}}s:1:"s";a:3:{s:1:"e";a:1:{s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8835;}s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8839;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10950;}}}}s:1:"n";a:1:{s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8843;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10956;}}}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10952;}}}s:1:"u";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10964;}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10966;}}}}}}s:1:"w";a:3:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8665;}}}}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10534;}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8601;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8601;}}}}}}s:1:"n";a:1:{s:1:"w";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10538;}}}}}}s:1:"z";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:223;}s:9:"codepoint";i:223;}}}}}s:1:"t";a:13:{s:1:"a";a:2:{s:1:"r";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8982;}}}}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:964;}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9140;}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:357;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:355;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1090;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8411;}}}}s:1:"e";a:1:{s:1:"l";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8981;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120113;}}}s:1:"h";a:4:{s:1:"e";a:2:{s:1:"r";a:1:{s:1:"e";a:2:{i:4;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8756;}}s:1:"f";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8756;}}}}}}}s:1:"t";a:1:{s:1:"a";a:3:{s:1:";";a:1:{s:9:"codepoint";i:952;}s:1:"s";a:1:{s:1:"y";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:977;}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:977;}}}}}s:1:"i";a:2:{s:1:"c";a:1:{s:1:"k";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8776;}}}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8764;}}}}}}s:1:"n";a:1:{s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8201;}}}}}s:1:"k";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8776;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8764;}}}}}s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:254;}s:9:"codepoint";i:254;}}}}s:1:"i";a:3:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:732;}}}}s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:215;}s:9:"codepoint";i:215;s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8864;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10801;}}}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10800;}}}}}s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8749;}}}}s:1:"o";a:3:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10536;}}}s:1:"p";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8868;}s:1:"b";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9014;}}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10993;}}}}s:1:"f";a:2:{s:1:";";a:1:{s:9:"codepoint";i:120165;}s:1:"o";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10970;}}}}}}s:1:"s";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10537;}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8244;}}}}}}s:1:"r";a:3:{s:1:"a";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8482;}}}}s:1:"i";a:7:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:5:{s:1:";";a:1:{s:9:"codepoint";i:9653;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9663;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9667;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8884;}}}}}}}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8796;}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9657;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8885;}}}}}}}}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9708;}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8796;}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10810;}}}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10809;}}}}}s:1:"s";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10701;}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10811;}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"z";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9186;}}}}}}}}s:1:"s";a:3:{s:1:"c";a:2:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120009;}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1094;}}}s:1:"h";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1115;}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:359;}}}}}}s:1:"w";a:2:{s:1:"i";a:1:{s:1:"x";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8812;}}}}s:1:"o";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"d";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8606;}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8608;}}}}}}}}}}}}}}}}}}s:1:"u";a:18:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8657;}}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10595;}}}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:250;}s:9:"codepoint";i:250;}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8593;}}}}s:1:"b";a:1:{s:1:"r";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1118;}}}s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:365;}}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:251;}s:9:"codepoint";i:251;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1091;}}}s:1:"d";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8645;}}}}s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:369;}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10606;}}}}}s:1:"f";a:2:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10622;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120114;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:249;}s:9:"codepoint";i:249;}}}}}s:1:"h";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8639;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8638;}}}}s:1:"b";a:1:{s:1:"l";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9600;}}}}}s:1:"l";a:2:{s:1:"c";a:2:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8988;}s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8988;}}}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8975;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9720;}}}}}s:1:"m";a:2:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:363;}}}}s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:168;}s:9:"codepoint";i:168;}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:371;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120166;}}}}s:1:"p";a:6:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8593;}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8597;}}}}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8639;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8638;}}}}}}}}}}}}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8846;}}}}s:1:"s";a:1:{s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:965;}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:978;}}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:965;}}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8648;}}}}}}}}}}s:1:"r";a:3:{s:1:"c";a:2:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8989;}s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8989;}}}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8974;}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:367;}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9721;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120010;}}}}s:1:"t";a:3:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8944;}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:361;}}}}}s:1:"r";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9653;}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9652;}}}}}s:1:"u";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8648;}}}}s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:252;}s:9:"codepoint";i:252;}}}s:1:"w";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10663;}}}}}}}}s:1:"v";a:14:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8661;}}}}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10984;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10985;}}}}}s:1:"D";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8872;}}}}}s:1:"a";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10652;}}}}}s:1:"r";a:7:{s:1:"e";a:1:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:949;}}}}}}}}s:1:"k";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1008;}}}}}}s:1:"n";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8709;}}}}}}}}s:1:"p";a:3:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:966;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:982;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8733;}}}}}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8597;}s:1:"h";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1009;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:962;}}}}}}s:1:"t";a:2:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:977;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8882;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8883;}}}}}}}}}}}}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1074;}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8866;}}}}}s:1:"e";a:3:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8744;}s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8891;}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8794;}}}}s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8942;}}}}}s:1:"r";a:2:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:124;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:124;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120115;}}}s:1:"l";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8882;}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120167;}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8733;}}}}}s:1:"r";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8883;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120011;}}}}s:1:"z";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"z";a:1:{s:1:"a";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10650;}}}}}}}}s:1:"w";a:7:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:373;}}}}}s:1:"e";a:2:{s:1:"d";a:2:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10847;}}}}s:1:"g";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8743;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8793;}}}}}s:1:"i";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8472;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120116;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120168;}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8472;}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8768;}s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8768;}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120012;}}}}}s:1:"x";a:14:{s:1:"c";a:3:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8898;}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9711;}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8899;}}}}s:1:"d";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9661;}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120117;}}}s:1:"h";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10234;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10231;}}}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:958;}}s:1:"l";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10232;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10229;}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10236;}}}}s:1:"n";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8955;}}}}s:1:"o";a:3:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10752;}}}}s:1:"p";a:2:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120169;}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10753;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10754;}}}}}}s:1:"r";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10233;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10230;}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120013;}}}s:1:"q";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10758;}}}}}}s:1:"u";a:2:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10756;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9651;}}}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8897;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8896;}}}}}}}s:1:"y";a:8:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:253;}s:9:"codepoint";i:253;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1103;}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:375;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1099;}}}s:1:"e";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:165;}s:9:"codepoint";i:165;}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120118;}}}s:1:"i";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1111;}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120170;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120014;}}}}s:1:"u";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1102;}}}s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:255;}s:9:"codepoint";i:255;}}}}s:1:"z";a:10:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:378;}}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:382;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1079;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:380;}}}}s:1:"e";a:2:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8488;}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:950;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120119;}}}s:1:"h";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1078;}}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8669;}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120171;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120015;}}}}s:1:"w";a:2:{s:1:"j";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8205;}}s:1:"n";a:1:{s:1:"j";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8204;}}}}}}
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceGmagick.php b/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceGmagick.php
deleted file mode 100644
index 5d41906e3..000000000
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceGmagick.php
+++ /dev/null
@@ -1,308 +0,0 @@
-
- * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
- */
-
-namespace Svg\Surface;
-
-use Svg\Style;
-
-class SurfaceGmagick implements SurfaceInterface
-{
- const DEBUG = false;
-
- /** @var \GmagickDraw */
- private $canvas;
-
- private $width;
- private $height;
-
- /** @var Style */
- private $style;
-
- public function __construct($w, $h)
- {
- if (self::DEBUG) {
- echo __FUNCTION__ . "\n";
- }
- $this->width = $w;
- $this->height = $h;
-
- $canvas = new \GmagickDraw();
-
- $this->canvas = $canvas;
- }
-
- function out()
- {
- if (self::DEBUG) {
- echo __FUNCTION__ . "\n";
- }
-
- $image = new \Gmagick();
- $image->newimage($this->width, $this->height);
- $image->drawimage($this->canvas);
-
- $tmp = tempnam("", "gm");
-
- $image->write($tmp);
-
- return file_get_contents($tmp);
- }
-
- public function save()
- {
- if (self::DEBUG) {
- echo __FUNCTION__ . "\n";
- }
- $this->canvas->save();
- }
-
- public function restore()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->restore();
- }
-
- public function scale($x, $y)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->scale($x, $y);
- }
-
- public function rotate($angle)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->rotate($angle);
- }
-
- public function translate($x, $y)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->translate($x, $y);
- }
-
- public function transform($a, $b, $c, $d, $e, $f)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->concat($a, $b, $c, $d, $e, $f);
- }
-
- public function beginPath()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- // TODO: Implement beginPath() method.
- }
-
- public function closePath()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->closepath();
- }
-
- public function fillStroke()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->fill_stroke();
- }
-
- public function clip()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->clip();
- }
-
- public function fillText($text, $x, $y, $maxWidth = null)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->set_text_pos($x, $y);
- $this->canvas->show($text);
- }
-
- public function strokeText($text, $x, $y, $maxWidth = null)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- // TODO: Implement drawImage() method.
- }
-
- public function drawImage($image, $sx, $sy, $sw = null, $sh = null, $dx = null, $dy = null, $dw = null, $dh = null)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
-
- if (strpos($image, "data:") === 0) {
- $data = substr($image, strpos($image, ";") + 1);
- if (strpos($data, "base64") === 0) {
- $data = base64_decode(substr($data, 7));
- }
-
- $image = tempnam("", "svg");
- file_put_contents($image, $data);
- }
-
- $img = $this->canvas->load_image("auto", $image, "");
-
- $sy = $sy - $sh;
- $this->canvas->fit_image($img, $sx, $sy, 'boxsize={' . "$sw $sh" . '} fitmethod=entire');
- }
-
- public function lineTo($x, $y)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->lineto($x, $y);
- }
-
- public function moveTo($x, $y)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->moveto($x, $y);
- }
-
- public function quadraticCurveTo($cpx, $cpy, $x, $y)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- // TODO: Implement quadraticCurveTo() method.
- }
-
- public function bezierCurveTo($cp1x, $cp1y, $cp2x, $cp2y, $x, $y)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->curveto($cp1x, $cp1y, $cp2x, $cp2y, $x, $y);
- }
-
- public function arcTo($x1, $y1, $x2, $y2, $radius)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- }
-
- public function arc($x, $y, $radius, $startAngle, $endAngle, $anticlockwise = false)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->arc($x, $y, $radius, $startAngle, $endAngle);
- }
-
- public function circle($x, $y, $radius)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->circle($x, $y, $radius);
- }
-
- public function ellipse($x, $y, $radiusX, $radiusY, $rotation, $startAngle, $endAngle, $anticlockwise)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->ellipse($x, $y, $radiusX, $radiusY);
- }
-
- public function fillRect($x, $y, $w, $h)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->rect($x, $y, $w, $h);
- $this->fill();
- }
-
- public function rect($x, $y, $w, $h, $rx = 0, $ry = 0)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->rect($x, $y, $w, $h);
- }
-
- public function fill()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->fill();
- }
-
- public function strokeRect($x, $y, $w, $h)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->rect($x, $y, $w, $h);
- $this->stroke();
- }
-
- public function stroke()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->stroke();
- }
-
- public function endPath()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- //$this->canvas->endPath();
- }
-
- public function measureText($text)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
- $style = $this->getStyle();
- $font = $this->getFont($style->fontFamily, $style->fontStyle);
-
- return $this->canvas->stringwidth($text, $font, $this->getStyle()->fontSize);
- }
-
- public function getStyle()
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
-
- return $this->style;
- }
-
- public function setStyle(Style $style)
- {
- if (self::DEBUG) echo __FUNCTION__ . "\n";
-
- $this->style = $style;
- $canvas = $this->canvas;
-
- if (is_array($style->stroke) && $stroke = $style->stroke) {
- $canvas->setcolor("stroke", "rgb", $stroke[0] / 255, $stroke[1] / 255, $stroke[2] / 255, null);
- }
-
- if (is_array($style->fill) && $fill = $style->fill) {
- // $canvas->setcolor("fill", "rgb", $fill[0] / 255, $fill[1] / 255, $fill[2] / 255, null);
- }
-
- $opts = array();
- if ($style->strokeWidth > 0.000001) {
- $opts[] = "linewidth=$style->strokeWidth";
- }
-
- if (in_array($style->strokeLinecap, array("butt", "round", "projecting"))) {
- $opts[] = "linecap=$style->strokeLinecap";
- }
-
- if (in_array($style->strokeLinejoin, array("miter", "round", "bevel"))) {
- $opts[] = "linejoin=$style->strokeLinejoin";
- }
-
- $canvas->set_graphics_option(implode(" ", $opts));
-
- $font = $this->getFont($style->fontFamily, $style->fontStyle);
- $canvas->setfont($font, $style->fontSize);
- }
-
- private function getFont($family, $style)
- {
- $map = array(
- "serif" => "Times",
- "sans-serif" => "Helvetica",
- "fantasy" => "Symbol",
- "cursive" => "serif",
- "monospance" => "Courier",
- );
-
- $family = strtolower($family);
- if (isset($map[$family])) {
- $family = $map[$family];
- }
-
- return $this->canvas->load_font($family, "unicode", "fontstyle=$style");
- }
-
- public function setFont($family, $style, $weight)
- {
- // TODO: Implement setFont() method.
- }
-}
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/autoload.php b/library/vendor/dompdf/lib/php-svg-lib/src/autoload.php
deleted file mode 100644
index b0e2b9cc4..000000000
--- a/library/vendor/dompdf/lib/php-svg-lib/src/autoload.php
+++ /dev/null
@@ -1,17 +0,0 @@
-
- * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
- */
-
-spl_autoload_register(function($class) {
- if (0 === strpos($class, "Svg")) {
- $file = str_replace('\\', DIRECTORY_SEPARATOR, $class);
- $file = realpath(__DIR__ . DIRECTORY_SEPARATOR . $file . '.php');
- if (file_exists($file)) {
- include_once $file;
- }
- }
-});
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/res/broken_image.svg b/library/vendor/dompdf/lib/res/broken_image.svg
deleted file mode 100644
index 09b97e6c3..000000000
--- a/library/vendor/dompdf/lib/res/broken_image.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/library/vendor/dompdf/src/Adapter/CPDF.php b/library/vendor/dompdf/src/Adapter/CPDF.php
deleted file mode 100644
index d976627b8..000000000
--- a/library/vendor/dompdf/src/Adapter/CPDF.php
+++ /dev/null
@@ -1,1184 +0,0 @@
-
- * @author Orion Richardson
- * @author Helmut Tischer
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-// FIXME: Need to sanity check inputs to this class
-namespace Dompdf\Adapter;
-
-use Dompdf\Canvas;
-use Dompdf\Dompdf;
-use Dompdf\Helpers;
-use Dompdf\Exception;
-use Dompdf\Image\Cache;
-use Dompdf\PhpEvaluator;
-
-/**
- * PDF rendering interface
- *
- * Dompdf\Adapter\CPDF provides a simple stateless interface to the stateful one
- * provided by the Cpdf class.
- *
- * Unless otherwise mentioned, all dimensions are in points (1/72 in). The
- * coordinate origin is in the top left corner, and y values increase
- * downwards.
- *
- * See {@link http://www.ros.co.nz/pdf/} for more complete documentation
- * on the underlying {@link Cpdf} class.
- *
- * @package dompdf
- */
-class CPDF implements Canvas
-{
-
- /**
- * Dimensions of paper sizes in points
- *
- * @var array;
- */
- static $PAPER_SIZES = array(
- "4a0" => array(0, 0, 4767.87, 6740.79),
- "2a0" => array(0, 0, 3370.39, 4767.87),
- "a0" => array(0, 0, 2383.94, 3370.39),
- "a1" => array(0, 0, 1683.78, 2383.94),
- "a2" => array(0, 0, 1190.55, 1683.78),
- "a3" => array(0, 0, 841.89, 1190.55),
- "a4" => array(0, 0, 595.28, 841.89),
- "a5" => array(0, 0, 419.53, 595.28),
- "a6" => array(0, 0, 297.64, 419.53),
- "a7" => array(0, 0, 209.76, 297.64),
- "a8" => array(0, 0, 147.40, 209.76),
- "a9" => array(0, 0, 104.88, 147.40),
- "a10" => array(0, 0, 73.70, 104.88),
- "b0" => array(0, 0, 2834.65, 4008.19),
- "b1" => array(0, 0, 2004.09, 2834.65),
- "b2" => array(0, 0, 1417.32, 2004.09),
- "b3" => array(0, 0, 1000.63, 1417.32),
- "b4" => array(0, 0, 708.66, 1000.63),
- "b5" => array(0, 0, 498.90, 708.66),
- "b6" => array(0, 0, 354.33, 498.90),
- "b7" => array(0, 0, 249.45, 354.33),
- "b8" => array(0, 0, 175.75, 249.45),
- "b9" => array(0, 0, 124.72, 175.75),
- "b10" => array(0, 0, 87.87, 124.72),
- "c0" => array(0, 0, 2599.37, 3676.54),
- "c1" => array(0, 0, 1836.85, 2599.37),
- "c2" => array(0, 0, 1298.27, 1836.85),
- "c3" => array(0, 0, 918.43, 1298.27),
- "c4" => array(0, 0, 649.13, 918.43),
- "c5" => array(0, 0, 459.21, 649.13),
- "c6" => array(0, 0, 323.15, 459.21),
- "c7" => array(0, 0, 229.61, 323.15),
- "c8" => array(0, 0, 161.57, 229.61),
- "c9" => array(0, 0, 113.39, 161.57),
- "c10" => array(0, 0, 79.37, 113.39),
- "ra0" => array(0, 0, 2437.80, 3458.27),
- "ra1" => array(0, 0, 1729.13, 2437.80),
- "ra2" => array(0, 0, 1218.90, 1729.13),
- "ra3" => array(0, 0, 864.57, 1218.90),
- "ra4" => array(0, 0, 609.45, 864.57),
- "sra0" => array(0, 0, 2551.18, 3628.35),
- "sra1" => array(0, 0, 1814.17, 2551.18),
- "sra2" => array(0, 0, 1275.59, 1814.17),
- "sra3" => array(0, 0, 907.09, 1275.59),
- "sra4" => array(0, 0, 637.80, 907.09),
- "letter" => array(0, 0, 612.00, 792.00),
- "half-letter" => array(0, 0, 396.00, 612.00),
- "legal" => array(0, 0, 612.00, 1008.00),
- "ledger" => array(0, 0, 1224.00, 792.00),
- "tabloid" => array(0, 0, 792.00, 1224.00),
- "executive" => array(0, 0, 521.86, 756.00),
- "folio" => array(0, 0, 612.00, 936.00),
- "commercial #10 envelope" => array(0, 0, 684, 297),
- "catalog #10 1/2 envelope" => array(0, 0, 648, 864),
- "8.5x11" => array(0, 0, 612.00, 792.00),
- "8.5x14" => array(0, 0, 612.00, 1008.0),
- "11x17" => array(0, 0, 792.00, 1224.00),
- );
-
- /**
- * The Dompdf object
- *
- * @var Dompdf
- */
- private $_dompdf;
-
- /**
- * Instance of Cpdf class
- *
- * @var Cpdf
- */
- private $_pdf;
-
- /**
- * PDF width, in points
- *
- * @var float
- */
- private $_width;
-
- /**
- * PDF height, in points
- *
- * @var float;
- */
- private $_height;
-
- /**
- * Current page number
- *
- * @var int
- */
- private $_page_number;
-
- /**
- * Total number of pages
- *
- * @var int
- */
- private $_page_count;
-
- /**
- * Text to display on every page
- *
- * @var array
- */
- private $_page_text;
-
- /**
- * Array of pages for accessing after rendering is initially complete
- *
- * @var array
- */
- private $_pages;
-
- /**
- * Array of temporary cached images to be deleted when processing is complete
- *
- * @var array
- */
- private $_image_cache;
-
- /**
- * Currently-applied opacity level (0 - 1)
- *
- * @var float
- */
- private $_current_opacity = 1;
-
- /**
- * Class constructor
- *
- * @param mixed $paper The size of paper to use in this PDF ({@link CPDF::$PAPER_SIZES})
- * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
- * @param Dompdf $dompdf The Dompdf instance
- */
- public function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf)
- {
- if (is_array($paper)) {
- $size = $paper;
- } else if (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) {
- $size = self::$PAPER_SIZES[mb_strtolower($paper)];
- } else {
- $size = self::$PAPER_SIZES["letter"];
- }
-
- if (mb_strtolower($orientation) === "landscape") {
- list($size[2], $size[3]) = array($size[3], $size[2]);
- }
-
- $this->_dompdf = $dompdf;
-
- $this->_pdf = new \Cpdf(
- $size,
- true,
- $dompdf->getOptions()->getFontCache(),
- $dompdf->getOptions()->getTempDir()
- );
-
- $this->_pdf->addInfo("Producer", sprintf("%s + CPDF", $dompdf->version));
- $time = substr_replace(date('YmdHisO'), '\'', -2, 0) . '\'';
- $this->_pdf->addInfo("CreationDate", "D:$time");
- $this->_pdf->addInfo("ModDate", "D:$time");
-
- $this->_width = $size[2] - $size[0];
- $this->_height = $size[3] - $size[1];
-
- $this->_page_number = $this->_page_count = 1;
- $this->_page_text = array();
-
- $this->_pages = array($this->_pdf->getFirstPageId());
-
- $this->_image_cache = array();
- }
-
- /**
- * @return Dompdf
- */
- public function get_dompdf()
- {
- return $this->_dompdf;
- }
-
- /**
- * Class destructor
- *
- * Deletes all temporary image files
- */
- public function __destruct()
- {
- foreach ($this->_image_cache as $img) {
- // The file might be already deleted by 3rd party tmp cleaner,
- // the file might not have been created at all
- // (if image outputting commands failed)
- // or because the destructor was called twice accidentally.
- if (!file_exists($img)) {
- continue;
- }
-
- if ($this->_dompdf->getOptions()->getDebugPng()) {
- print '[__destruct unlink ' . $img . ']';
- }
- if (!$this->_dompdf->getOptions()->getDebugKeepTemp()) {
- unlink($img);
- }
- }
- }
-
- /**
- * Returns the Cpdf instance
- *
- * @return \Cpdf
- */
- public function get_cpdf()
- {
- return $this->_pdf;
- }
-
- /**
- * Add meta information to the PDF
- *
- * @param string $label label of the value (Creator, Producer, etc.)
- * @param string $value the text to set
- */
- public function add_info($label, $value)
- {
- $this->_pdf->addInfo($label, $value);
- }
-
- /**
- * Opens a new 'object'
- *
- * While an object is open, all drawing actions are recorded in the object,
- * as opposed to being drawn on the current page. Objects can be added
- * later to a specific page or to several pages.
- *
- * The return value is an integer ID for the new object.
- *
- * @see CPDF::close_object()
- * @see CPDF::add_object()
- *
- * @return int
- */
- public function open_object()
- {
- $ret = $this->_pdf->openObject();
- $this->_pdf->saveState();
- return $ret;
- }
-
- /**
- * Reopens an existing 'object'
- *
- * @see CPDF::open_object()
- * @param int $object the ID of a previously opened object
- */
- public function reopen_object($object)
- {
- $this->_pdf->reopenObject($object);
- $this->_pdf->saveState();
- }
-
- /**
- * Closes the current 'object'
- *
- * @see CPDF::open_object()
- */
- public function close_object()
- {
- $this->_pdf->restoreState();
- $this->_pdf->closeObject();
- }
-
- /**
- * Adds a specified 'object' to the document
- *
- * $object int specifying an object created with {@link
- * CPDF::open_object()}. $where can be one of:
- * - 'add' add to current page only
- * - 'all' add to every page from the current one onwards
- * - 'odd' add to all odd numbered pages from now on
- * - 'even' add to all even numbered pages from now on
- * - 'next' add the object to the next page only
- * - 'nextodd' add to all odd numbered pages from the next one
- * - 'nexteven' add to all even numbered pages from the next one
- *
- * @see Cpdf::addObject()
- *
- * @param int $object
- * @param string $where
- */
- public function add_object($object, $where = 'all')
- {
- $this->_pdf->addObject($object, $where);
- }
-
- /**
- * Stops the specified 'object' from appearing in the document.
- *
- * The object will stop being displayed on the page following the current
- * one.
- *
- * @param int $object
- */
- public function stop_object($object)
- {
- $this->_pdf->stopObject($object);
- }
-
- /**
- * @access private
- */
- public function serialize_object($id)
- {
- // Serialize the pdf object's current state for retrieval later
- return $this->_pdf->serializeObject($id);
- }
-
- /**
- * @access private
- */
- public function reopen_serialized_object($obj)
- {
- return $this->_pdf->restoreSerializedObject($obj);
- }
-
- //........................................................................
-
- /**
- * Returns the PDF's width in points
- * @return float
- */
- public function get_width()
- {
- return $this->_width;
- }
-
- /**
- * Returns the PDF's height in points
- * @return float
- */
- public function get_height()
- {
- return $this->_height;
- }
-
- /**
- * Returns the current page number
- * @return int
- */
- public function get_page_number()
- {
- return $this->_page_number;
- }
-
- /**
- * Returns the total number of pages in the document
- * @return int
- */
- public function get_page_count()
- {
- return $this->_page_count;
- }
-
- /**
- * Sets the current page number
- *
- * @param int $num
- */
- public function set_page_number($num)
- {
- $this->_page_number = $num;
- }
-
- /**
- * Sets the page count
- *
- * @param int $count
- */
- public function set_page_count($count)
- {
- $this->_page_count = $count;
- }
-
- /**
- * Sets the stroke color
- *
- * See {@link Style::set_color()} for the format of the color array.
- * @param array $color
- */
- protected function _set_stroke_color($color)
- {
- $this->_pdf->setStrokeColor($color);
- $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
- if ($this->_current_opacity != 1) {
- $alpha *= $this->_current_opacity;
- }
- $this->_set_line_transparency("Normal", $alpha);
- }
-
- /**
- * Sets the fill colour
- *
- * See {@link Style::set_color()} for the format of the colour array.
- * @param array $color
- */
- protected function _set_fill_color($color)
- {
- $this->_pdf->setColor($color);
- $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
- if ($this->_current_opacity) {
- $alpha *= $this->_current_opacity;
- }
- $this->_set_fill_transparency("Normal", $alpha);
- }
-
- /**
- * Sets line transparency
- * @see Cpdf::setLineTransparency()
- *
- * Valid blend modes are (case-sensitive):
- *
- * Normal, Multiply, Screen, Overlay, Darken, Lighten,
- * ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
- * Exclusion
- *
- * @param string $mode the blending mode to use
- * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
- */
- protected function _set_line_transparency($mode, $opacity)
- {
- $this->_pdf->setLineTransparency($mode, $opacity);
- }
-
- /**
- * Sets fill transparency
- * @see Cpdf::setFillTransparency()
- *
- * Valid blend modes are (case-sensitive):
- *
- * Normal, Multiply, Screen, Overlay, Darken, Lighten,
- * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
- * Exclusion
- *
- * @param string $mode the blending mode to use
- * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
- */
- protected function _set_fill_transparency($mode, $opacity)
- {
- $this->_pdf->setFillTransparency($mode, $opacity);
- }
-
- /**
- * Sets the line style
- *
- * @see Cpdf::setLineStyle()
- *
- * @param float $width
- * @param string $cap
- * @param string $join
- * @param array $dash
- */
- protected function _set_line_style($width, $cap, $join, $dash)
- {
- $this->_pdf->setLineStyle($width, $cap, $join, $dash);
- }
-
- /**
- * Sets the opacity
- *
- * @param $opacity
- * @param $mode
- */
- public function set_opacity($opacity, $mode = "Normal")
- {
- $this->_set_line_transparency($mode, $opacity);
- $this->_set_fill_transparency($mode, $opacity);
- $this->_current_opacity = $opacity;
- }
-
- public function set_default_view($view, $options = array())
- {
- array_unshift($options, $view);
- call_user_func_array(array($this->_pdf, "openHere"), $options);
- }
-
- /**
- * Remaps y coords from 4th to 1st quadrant
- *
- * @param float $y
- * @return float
- */
- protected function y($y)
- {
- return $this->_height - $y;
- }
-
- /**
- * Canvas implementation
- *
- * @param float $x1
- * @param float $y1
- * @param float $x2
- * @param float $y2
- * @param array $color
- * @param float $width
- * @param array $style
- */
- public function line($x1, $y1, $x2, $y2, $color, $width, $style = array())
- {
- $this->_set_stroke_color($color);
- $this->_set_line_style($width, "butt", "", $style);
-
- $this->_pdf->line($x1, $this->y($y1),
- $x2, $this->y($y2));
- $this->_set_line_transparency("Normal", $this->_current_opacity);
- }
-
- /**
- * Draw line at the specified coordinates on every page.
- *
- * See {@link Style::munge_color()} for the format of the colour array.
- *
- * @param float $x1
- * @param float $y1
- * @param float $x2
- * @param float $y2
- * @param array $color
- * @param float $width
- * @param array $style optional
- */
- public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = array())
- {
- $_t = 'line';
- $this->_page_text[] = compact('_t', 'x1', 'y1', 'x2', 'y2', 'color', 'width', 'style');
- }
-
- /**
- * @param float $x
- * @param float $y
- * @param float $r1
- * @param float $r2
- * @param float $astart
- * @param float $aend
- * @param array $color
- * @param float $width
- * @param array $style
- */
- public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array())
- {
- $this->_set_stroke_color($color);
- $this->_set_line_style($width, "butt", "", $style);
-
- $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
- $this->_set_line_transparency("Normal", $this->_current_opacity);
- }
-
- /**
- * Convert a GIF or BMP image to a PNG image
- *
- * @param string $image_url
- * @param integer $type
- *
- * @throws Exception
- * @return string The url of the newly converted image
- */
- protected function _convert_gif_bmp_to_png($image_url, $type)
- {
- $func_name = "imagecreatefrom$type";
-
- if (!function_exists($func_name)) {
- if (!method_exists("Dompdf\Helpers", $func_name)) {
- throw new Exception("Function $func_name() not found. Cannot convert $type image: $image_url. Please install the image PHP extension.");
- }
- $func_name = "\\Dompdf\\Helpers::" . $func_name;
- }
-
- set_error_handler(array("\\Dompdf\\Helpers", "record_warnings"));
- $im = call_user_func($func_name, $image_url);
-
- if ($im) {
- imageinterlace($im, false);
-
- $tmp_dir = $this->_dompdf->getOptions()->getTempDir();
- $tmp_name = @tempnam($tmp_dir, "{$type}dompdf_img_");
- @unlink($tmp_name);
- $filename = "$tmp_name.png";
- $this->_image_cache[] = $filename;
-
- imagepng($im, $filename);
- imagedestroy($im);
- } else {
- $filename = Cache::$broken_image;
- }
-
- restore_error_handler();
-
- return $filename;
- }
-
- /**
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param array $color
- * @param float $width
- * @param array $style
- */
- public function rectangle($x1, $y1, $w, $h, $color, $width, $style = array())
- {
- $this->_set_stroke_color($color);
- $this->_set_line_style($width, "butt", "", $style);
- $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
- $this->_set_line_transparency("Normal", $this->_current_opacity);
- }
-
- /**
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param array $color
- */
- public function filled_rectangle($x1, $y1, $w, $h, $color)
- {
- $this->_set_fill_color($color);
- $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
- $this->_set_fill_transparency("Normal", $this->_current_opacity);
- }
-
- /**
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- */
- public function clipping_rectangle($x1, $y1, $w, $h)
- {
- $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
- }
-
- /**
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param float $rTL
- * @param float $rTR
- * @param float $rBR
- * @param float $rBL
- */
- public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
- {
- $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
- }
-
- /**
- *
- */
- public function clipping_end()
- {
- $this->_pdf->clippingEnd();
- }
-
- /**
- *
- */
- public function save()
- {
- $this->_pdf->saveState();
- }
-
- /**
- *
- */
- public function restore()
- {
- $this->_pdf->restoreState();
- }
-
- /**
- * @param $angle
- * @param $x
- * @param $y
- */
- public function rotate($angle, $x, $y)
- {
- $this->_pdf->rotate($angle, $x, $y);
- }
-
- /**
- * @param $angle_x
- * @param $angle_y
- * @param $x
- * @param $y
- */
- public function skew($angle_x, $angle_y, $x, $y)
- {
- $this->_pdf->skew($angle_x, $angle_y, $x, $y);
- }
-
- /**
- * @param $s_x
- * @param $s_y
- * @param $x
- * @param $y
- */
- public function scale($s_x, $s_y, $x, $y)
- {
- $this->_pdf->scale($s_x, $s_y, $x, $y);
- }
-
- /**
- * @param $t_x
- * @param $t_y
- */
- public function translate($t_x, $t_y)
- {
- $this->_pdf->translate($t_x, $t_y);
- }
-
- /**
- * @param $a
- * @param $b
- * @param $c
- * @param $d
- * @param $e
- * @param $f
- */
- public function transform($a, $b, $c, $d, $e, $f)
- {
- $this->_pdf->transform(array($a, $b, $c, $d, $e, $f));
- }
-
- /**
- * @param array $points
- * @param array $color
- * @param null $width
- * @param array $style
- * @param bool $fill
- */
- public function polygon($points, $color, $width = null, $style = array(), $fill = false)
- {
- $this->_set_fill_color($color);
- $this->_set_stroke_color($color);
-
- // Adjust y values
- for ($i = 1; $i < count($points); $i += 2) {
- $points[$i] = $this->y($points[$i]);
- }
-
- $this->_pdf->polygon($points, count($points) / 2, $fill);
-
- $this->_set_fill_transparency("Normal", $this->_current_opacity);
- $this->_set_line_transparency("Normal", $this->_current_opacity);
- }
-
- /**
- * @param float $x
- * @param float $y
- * @param float $r1
- * @param array $color
- * @param null $width
- * @param null $style
- * @param bool $fill
- */
- public function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false)
- {
- $this->_set_fill_color($color);
- $this->_set_stroke_color($color);
-
- if (!$fill && isset($width)) {
- $this->_set_line_style($width, "round", "round", $style);
- }
-
- $this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill);
-
- $this->_set_fill_transparency("Normal", $this->_current_opacity);
- $this->_set_line_transparency("Normal", $this->_current_opacity);
- }
-
- /**
- * @param string $img
- * @param float $x
- * @param float $y
- * @param int $w
- * @param int $h
- * @param string $resolution
- */
- public function image($img, $x, $y, $w, $h, $resolution = "normal")
- {
- list($width, $height, $type) = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
-
- $debug_png = $this->_dompdf->getOptions()->getDebugPng();
-
- if ($debug_png) {
- print "[image:$img|$width|$height|$type]";
- }
-
- switch ($type) {
- case "jpeg":
- if ($debug_png) {
- print '!!!jpg!!!';
- }
- $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
- break;
-
- case "gif":
- /** @noinspection PhpMissingBreakStatementInspection */
- case "bmp":
- if ($debug_png) print '!!!bmp or gif!!!';
- // @todo use cache for BMP and GIF
- $img = $this->_convert_gif_bmp_to_png($img, $type);
-
- case "png":
- if ($debug_png) print '!!!png!!!';
-
- $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
- break;
-
- case "svg":
- if ($debug_png) print '!!!SVG!!!';
-
- $this->_pdf->addSvgFromFile($img, $x, $this->y($y) - $h, $w, $h);
- break;
-
- default:
- if ($debug_png) print '!!!unknown!!!';
- }
- }
-
- /**
- * @param float $x
- * @param float $y
- * @param string $text
- * @param string $font
- * @param float $size
- * @param array $color
- * @param float $word_space
- * @param float $char_space
- * @param float $angle
- */
- public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
- {
- $pdf = $this->_pdf;
-
- $this->_set_fill_color($color);
-
- $font .= ".afm";
- $pdf->selectFont($font);
-
- //FontMetrics::getFontHeight($font, $size) ==
- //$this->getFontHeight($font, $size) ==
- //$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size)
- //- FontBBoxheight+FontHeightOffset, scaled to $size, in pt
- //$this->_pdf->getFontDescender($size)
- //- Descender scaled to size
- //
- //$this->_pdf->fonts[$this->_pdf->currentFont] sizes:
- //['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top
- //Maximum extent of all glyphs of the font from the baseline point
- //['Ascender'] maximum height above baseline except accents
- //['Descender'] maximum depth below baseline, negative number means below baseline
- //['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used.
- //Values are in 1/1000 pt for a font size of 1 pt
- //
- //['FontBBox'][1] should be close to ['Descender']
- //['FontBBox'][3] should be close to ['Ascender']+Accents
- //in practice, FontBBox values are a little bigger
- //
- //The text position is referenced to the baseline, not to the lower corner of the FontBBox,
- //for what the left,top corner is given.
- //FontBBox spans also the background box for the text.
- //If the lower corner would be used as reference point, the Descents of the glyphs would
- //hang over the background box border.
- //Therefore compensate only the extent above the Baseline.
- //
- //print '['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].'] ';
- //
- //$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space);
- $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
-
- $this->_set_fill_transparency("Normal", $this->_current_opacity);
- }
-
- /**
- * @param string $code
- */
- public function javascript($code)
- {
- $this->_pdf->addJavascript($code);
- }
-
- //........................................................................
-
- /**
- * Add a named destination (similar to ... in html)
- *
- * @param string $anchorname The name of the named destination
- */
- public function add_named_dest($anchorname)
- {
- $this->_pdf->addDestination($anchorname, "Fit");
- }
-
- /**
- * Add a link to the pdf
- *
- * @param string $url The url to link to
- * @param float $x The x position of the link
- * @param float $y The y position of the link
- * @param float $width The width of the link
- * @param float $height The height of the link
- */
- public function add_link($url, $x, $y, $width, $height)
- {
- $y = $this->y($y) - $height;
-
- if (strpos($url, '#') === 0) {
- // Local link
- $name = substr($url, 1);
- if ($name) {
- $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
- }
- } else {
- $this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height);
- }
- }
-
- /**
- * @param string $text
- * @param string $font
- * @param float $size
- * @param int $word_spacing
- * @param int $char_spacing
- * @return float|int
- */
- public function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0)
- {
- $this->_pdf->selectFont($font);
- return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
- }
-
- /**
- * @param $font
- * @param $string
- */
- public function register_string_subset($font, $string)
- {
- $this->_pdf->registerText($font, $string);
- }
-
- /**
- * @param string $font
- * @param float $size
- * @return float|int
- */
- public function get_font_height($font, $size)
- {
- $this->_pdf->selectFont($font);
-
- $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
- return $this->_pdf->getFontHeight($size) * $ratio;
- }
-
- /*function get_font_x_height($font, $size) {
- $this->_pdf->selectFont($font);
- $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
- return $this->_pdf->getFontXHeight($size) * $ratio;
- }*/
-
- /**
- * @param string $font
- * @param float $size
- * @return float
- */
- public function get_font_baseline($font, $size)
- {
- $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
- return $this->get_font_height($font, $size) / $ratio;
- }
-
- /**
- * Writes text at the specified x and y coordinates on every page
- *
- * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
- * with their current values.
- *
- * See {@link Style::munge_color()} for the format of the colour array.
- *
- * @param float $x
- * @param float $y
- * @param string $text the text to write
- * @param string $font the font file to use
- * @param float $size the font size, in points
- * @param array $color
- * @param float $word_space word spacing adjustment
- * @param float $char_space char spacing adjustment
- * @param float $angle angle to write the text at, measured CW starting from the x-axis
- */
- public function page_text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
- {
- $_t = "text";
- $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
- }
-
- /**
- * Processes a script on every page
- *
- * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
- *
- * This function can be used to add page numbers to all pages
- * after the first one, for example.
- *
- * @param string $code the script code
- * @param string $type the language type for script
- */
- public function page_script($code, $type = "text/php")
- {
- $_t = "script";
- $this->_page_text[] = compact("_t", "code", "type");
- }
-
- /**
- * @return int
- */
- public function new_page()
- {
- $this->_page_number++;
- $this->_page_count++;
-
- $ret = $this->_pdf->newPage();
- $this->_pages[] = $ret;
- return $ret;
- }
-
- /**
- * Add text to each page after rendering is complete
- */
- protected function _add_page_text()
- {
- if (!count($this->_page_text)) {
- return;
- }
-
- $page_number = 1;
- $eval = null;
-
- foreach ($this->_pages as $pid) {
- $this->reopen_object($pid);
-
- foreach ($this->_page_text as $pt) {
- extract($pt);
-
- switch ($_t) {
- case "text":
- $text = str_replace(array("{PAGE_NUM}", "{PAGE_COUNT}"),
- array($page_number, $this->_page_count), $text);
- $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
- break;
-
- case "script":
- if (!$eval) {
- $eval = new PhpEvaluator($this);
- }
- $eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count));
- break;
-
- case 'line':
- $this->line( $x1, $y1, $x2, $y2, $color, $width, $style );
- break;
- }
- }
-
- $this->close_object();
- $page_number++;
- }
- }
-
- /**
- * Streams the PDF to the client.
- *
- * @param string $filename The filename to present to the client.
- * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
- */
- public function stream($filename = "document.pdf", $options = array())
- {
- if (headers_sent()) {
- die("Unable to stream pdf: headers already sent");
- }
-
- if (!isset($options["compress"])) $options["compress"] = true;
- if (!isset($options["Attachment"])) $options["Attachment"] = true;
-
- $this->_add_page_text();
-
- $debug = !$options['compress'];
- $tmp = ltrim($this->_pdf->output($debug));
-
- header("Cache-Control: private");
- header("Content-Type: application/pdf");
- header("Content-Length: " . mb_strlen($tmp, "8bit"));
-
- $filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
- $attachment = $options["Attachment"] ? "attachment" : "inline";
- header(Helpers::buildContentDispositionHeader($attachment, $filename));
-
- echo $tmp;
- flush();
- }
-
- /**
- * Returns the PDF as a string.
- *
- * @param array $options Associative array: 'compress' => 1 or 0 (default 1).
- * @return string
- */
- public function output($options = array())
- {
- if (!isset($options["compress"])) $options["compress"] = true;
-
- $this->_add_page_text();
-
- $debug = !$options['compress'];
-
- return $this->_pdf->output($debug);
- }
-
- /**
- * Returns logging messages generated by the Cpdf class
- *
- * @return string
- */
- public function get_messages()
- {
- return $this->_pdf->messages;
- }
-}
diff --git a/library/vendor/dompdf/src/Autoloader.php b/library/vendor/dompdf/src/Autoloader.php
deleted file mode 100644
index 08a3b4679..000000000
--- a/library/vendor/dompdf/src/Autoloader.php
+++ /dev/null
@@ -1,42 +0,0 @@
-
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-namespace Dompdf;
-
-/**
- * Main rendering interface
- *
- * Currently {@link Dompdf\Adapter\CPDF}, {@link Dompdf\Adapter\PDFLib}, and {@link Dompdf\Adapter\GD}
- * implement this interface.
- *
- * Implementations should measure x and y increasing to the left and down,
- * respectively, with the origin in the top left corner. Implementations
- * are free to use a unit other than points for length, but I can't
- * guarantee that the results will look any good.
- *
- * @package dompdf
- */
-interface Canvas
-{
- function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf);
-
- /**
- * @return Dompdf
- */
- function get_dompdf();
-
- /**
- * Returns the current page number
- *
- * @return int
- */
- function get_page_number();
-
- /**
- * Returns the total number of pages
- *
- * @return int
- */
- function get_page_count();
-
- /**
- * Sets the total number of pages
- *
- * @param int $count
- */
- function set_page_count($count);
-
- /**
- * Draws a line from x1,y1 to x2,y2
- *
- * See {@link Style::munge_color()} for the format of the color array.
- * See {@link Cpdf::setLineStyle()} for a description of the format of the
- * $style parameter (aka dash).
- *
- * @param float $x1
- * @param float $y1
- * @param float $x2
- * @param float $y2
- * @param array $color
- * @param float $width
- * @param array $style
- */
- function line($x1, $y1, $x2, $y2, $color, $width, $style = null);
-
- /**
- * Draws a rectangle at x1,y1 with width w and height h
- *
- * See {@link Style::munge_color()} for the format of the color array.
- * See {@link Cpdf::setLineStyle()} for a description of the $style
- * parameter (aka dash)
- *
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param array $color
- * @param float $width
- * @param array $style
- */
- function rectangle($x1, $y1, $w, $h, $color, $width, $style = null);
-
- /**
- * Draws a filled rectangle at x1,y1 with width w and height h
- *
- * See {@link Style::munge_color()} for the format of the color array.
- *
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param array $color
- */
- function filled_rectangle($x1, $y1, $w, $h, $color);
-
- /**
- * Starts a clipping rectangle at x1,y1 with width w and height h
- *
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- */
- function clipping_rectangle($x1, $y1, $w, $h);
-
- /**
- * Starts a rounded clipping rectangle at x1,y1 with width w and height h
- *
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param float $tl
- * @param float $tr
- * @param float $br
- * @param float $bl
- *
- * @return
- */
- function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
-
- /**
- * Ends the last clipping shape
- */
- function clipping_end();
-
- /**
- * Save current state
- */
- function save();
-
- /**
- * Restore last state
- */
- function restore();
-
- /**
- * Rotate
- *
- * @param float $angle angle in degrees for counter-clockwise rotation
- * @param float $x Origin abscissa
- * @param float $y Origin ordinate
- */
- function rotate($angle, $x, $y);
-
- /**
- * Skew
- *
- * @param float $angle_x
- * @param float $angle_y
- * @param float $x Origin abscissa
- * @param float $y Origin ordinate
- */
- function skew($angle_x, $angle_y, $x, $y);
-
- /**
- * Scale
- *
- * @param float $s_x scaling factor for width as percent
- * @param float $s_y scaling factor for height as percent
- * @param float $x Origin abscissa
- * @param float $y Origin ordinate
- */
- function scale($s_x, $s_y, $x, $y);
-
- /**
- * Translate
- *
- * @param float $t_x movement to the right
- * @param float $t_y movement to the bottom
- */
- function translate($t_x, $t_y);
-
- /**
- * Transform
- *
- * @param $a
- * @param $b
- * @param $c
- * @param $d
- * @param $e
- * @param $f
- * @return
- */
- function transform($a, $b, $c, $d, $e, $f);
-
- /**
- * Draws a polygon
- *
- * The polygon is formed by joining all the points stored in the $points
- * array. $points has the following structure:
- *
- * array(0 => x1,
- * 1 => y1,
- * 2 => x2,
- * 3 => y2,
- * ...
- * );
- *
- *
- * See {@link Style::munge_color()} for the format of the color array.
- * See {@link Cpdf::setLineStyle()} for a description of the $style
- * parameter (aka dash)
- *
- * @param array $points
- * @param array $color
- * @param float $width
- * @param array $style
- * @param bool $fill Fills the polygon if true
- */
- function polygon($points, $color, $width = null, $style = null, $fill = false);
-
- /**
- * Draws a circle at $x,$y with radius $r
- *
- * See {@link Style::munge_color()} for the format of the color array.
- * See {@link Cpdf::setLineStyle()} for a description of the $style
- * parameter (aka dash)
- *
- * @param float $x
- * @param float $y
- * @param float $r
- * @param array $color
- * @param float $width
- * @param array $style
- * @param bool $fill Fills the circle if true
- */
- function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false);
-
- /**
- * Add an image to the pdf.
- *
- * The image is placed at the specified x and y coordinates with the
- * given width and height.
- *
- * @param string $img_url the path to the image
- * @param float $x x position
- * @param float $y y position
- * @param int $w width (in pixels)
- * @param int $h height (in pixels)
- * @param string $resolution The resolution of the image
- */
- function image($img_url, $x, $y, $w, $h, $resolution = "normal");
-
- /**
- * Add an arc to the PDF
- * See {@link Style::munge_color()} for the format of the color array.
- *
- * @param float $x X coordinate of the arc
- * @param float $y Y coordinate of the arc
- * @param float $r1 Radius 1
- * @param float $r2 Radius 2
- * @param float $astart Start angle in degrees
- * @param float $aend End angle in degrees
- * @param array $color Color
- * @param float $width
- * @param array $style
- */
- function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array());
-
- /**
- * Writes text at the specified x and y coordinates
- * See {@link Style::munge_color()} for the format of the color array.
- *
- * @param float $x
- * @param float $y
- * @param string $text the text to write
- * @param string $font the font file to use
- * @param float $size the font size, in points
- * @param array $color
- * @param float $word_space word spacing adjustment
- * @param float $char_space char spacing adjustment
- * @param float $angle angle
- */
- function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
-
- /**
- * Add a named destination (similar to ... in html)
- *
- * @param string $anchorname The name of the named destination
- */
- function add_named_dest($anchorname);
-
- /**
- * Add a link to the pdf
- *
- * @param string $url The url to link to
- * @param float $x The x position of the link
- * @param float $y The y position of the link
- * @param float $width The width of the link
- * @param float $height The height of the link
- */
- function add_link($url, $x, $y, $width, $height);
-
- /**
- * Add meta information to the pdf
- *
- * @param string $name Label of the value (Creator, Producer, etc.)
- * @param string $value The text to set
- */
- function add_info($name, $value);
-
- /**
- * Calculates text size, in points
- *
- * @param string $text the text to be sized
- * @param string $font the desired font
- * @param float $size the desired font size
- * @param float $word_spacing word spacing, if any
- * @param float $char_spacing
- *
- * @return float
- */
- function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0);
-
- /**
- * Calculates font height, in points
- *
- * @param string $font
- * @param float $size
- *
- * @return float
- */
- function get_font_height($font, $size);
-
- /**
- * Calculates font baseline, in points
- *
- * @param string $font
- * @param float $size
- *
- * @return float
- */
- function get_font_baseline($font, $size);
-
- /**
- * Returns the PDF's width in points
- *
- * @return float
- */
- function get_width();
-
-
- /**
- * Return the image's height in pixels
- *
- * @return float
- */
- function get_height();
-
- /**
- * Returns the font x-height, in points
- *
- * @param string $font
- * @param float $size
- *
- * @return float
- */
- //function get_font_x_height($font, $size);
-
- /**
- * Sets the opacity
- *
- * @param float $opacity
- * @param string $mode
- */
- function set_opacity($opacity, $mode = "Normal");
-
- /**
- * Sets the default view
- *
- * @param string $view
- * 'XYZ' left, top, zoom
- * 'Fit'
- * 'FitH' top
- * 'FitV' left
- * 'FitR' left,bottom,right
- * 'FitB'
- * 'FitBH' top
- * 'FitBV' left
- * @param array $options
- *
- * @return void
- */
- function set_default_view($view, $options = array());
-
- /**
- * @param string $script
- *
- * @return void
- */
- function javascript($script);
-
- /**
- * Starts a new page
- *
- * Subsequent drawing operations will appear on the new page.
- */
- function new_page();
-
- /**
- * Streams the PDF directly to the browser.
- *
- * @param string $filename The filename to present to the browser.
- * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
- */
- function stream($filename, $options = array());
-
- /**
- * Returns the PDF as a string.
- *
- * @param array $options Associative array: 'compress' => 1 or 0 (default 1).
- * @return string
- */
- function output($options = array());
-}
diff --git a/library/vendor/dompdf/src/Css/Style.php b/library/vendor/dompdf/src/Css/Style.php
deleted file mode 100644
index 96ac5d42b..000000000
--- a/library/vendor/dompdf/src/Css/Style.php
+++ /dev/null
@@ -1,2952 +0,0 @@
-
- * @author Helmut Tischer
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\Css;
-
-use Dompdf\Adapter\CPDF;
-use Dompdf\Exception;
-use Dompdf\Helpers;
-use Dompdf\FontMetrics;
-use Dompdf\Frame;
-
-/**
- * Represents CSS properties.
- *
- * The Style class is responsible for handling and storing CSS properties.
- * It includes methods to resolve colors and lengths, as well as getters &
- * setters for many CSS properites.
- *
- * Actual CSS parsing is performed in the {@link Stylesheet} class.
- *
- * @package dompdf
- */
-class Style
-{
-
- const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
- const CSS_INTEGER = "-?\d+";
-
- /**
- * Default font size, in points.
- *
- * @var float
- */
- static $default_font_size = 12;
-
- /**
- * Default line height, as a fraction of the font size.
- *
- * @var float
- */
- static $default_line_height = 1.2;
-
- /**
- * Default "absolute" font sizes relative to the default font-size
- * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property
- * @var array
- */
- static $font_size_keywords = array(
- "xx-small" => 0.6, // 3/5
- "x-small" => 0.75, // 3/4
- "small" => 0.889, // 8/9
- "medium" => 1, // 1
- "large" => 1.2, // 6/5
- "x-large" => 1.5, // 3/2
- "xx-large" => 2.0, // 2/1
- );
-
- /**
- * List of valid vertical-align keywords. Should also really be a constant.
- *
- * @var array
- */
- static $vertical_align_keywords = array("baseline", "bottom", "middle", "sub",
- "super", "text-bottom", "text-top", "top");
-
- /**
- * List of all inline types. Should really be a constant.
- *
- * @var array
- */
- static $INLINE_TYPES = array("inline");
-
- /**
- * List of all block types. Should really be a constant.
- *
- * @var array
- */
- static $BLOCK_TYPES = array("block", "inline-block", "table-cell", "list-item");
-
- /**
- * List of all positionned types. Should really be a constant.
- *
- * @var array
- */
- static $POSITIONNED_TYPES = array("relative", "absolute", "fixed");
-
- /**
- * List of all table types. Should really be a constant.
- *
- * @var array;
- */
- static $TABLE_TYPES = array("table", "inline-table");
-
- /**
- * List of valid border styles. Should also really be a constant.
- *
- * @var array
- */
- static $BORDER_STYLES = array("none", "hidden", "dotted", "dashed", "solid",
- "double", "groove", "ridge", "inset", "outset");
-
- /**
- * Default style values.
- *
- * @link http://www.w3.org/TR/CSS21/propidx.html
- *
- * @var array
- */
- static protected $_defaults = null;
-
- /**
- * List of inherited properties
- *
- * @link http://www.w3.org/TR/CSS21/propidx.html
- *
- * @var array
- */
- static protected $_inherited = null;
-
- /**
- * Caches method_exists result
- *
- * @var array
- */
- static protected $_methods_cache = array();
-
- /**
- * The stylesheet this style belongs to
- *
- * @see Stylesheet
- * @var Stylesheet
- */
- protected $_stylesheet; // stylesheet this style is attached to
-
- /**
- * Media queries attached to the style
- *
- * @var int
- */
- protected $_media_queries;
-
- /**
- * Main array of all CSS properties & values
- *
- * @var array
- */
- protected $_props;
-
- /* var instead of protected would allow access outside of class */
- protected $_important_props;
-
- /**
- * Cached property values
- *
- * @var array
- */
- protected $_prop_cache;
-
- /**
- * Font size of parent element in document tree. Used for relative font
- * size resolution.
- *
- * @var float
- */
- protected $_parent_font_size; // Font size of parent element
-
- protected $_font_family;
-
- /**
- * @var Frame
- */
- protected $_frame;
-
- /**
- * The origin of the style
- *
- * @var int
- */
- protected $_origin = Stylesheet::ORIG_AUTHOR;
-
- // private members
- /**
- * True once the font size is resolved absolutely
- *
- * @var bool
- */
- private $__font_size_calculated; // Cache flag
-
- /**
- * The computed bottom spacing
- */
- private $_computed_bottom_spacing = null;
-
- /**
- * The computed border radius
- */
- private $_computed_border_radius = null;
-
- /**
- * @var bool
- */
- public $_has_border_radius = false;
-
- /**
- * @var FontMetrics
- */
- private $fontMetrics;
-
- /**
- * Class constructor
- *
- * @param Stylesheet $stylesheet the stylesheet this Style is associated with.
- * @param int $origin
- */
- public function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR)
- {
- $this->setFontMetrics($stylesheet->getFontMetrics());
-
- $this->_props = array();
- $this->_important_props = array();
- $this->_stylesheet = $stylesheet;
- $this->_media_queries = array();
- $this->_origin = $origin;
- $this->_parent_font_size = null;
- $this->__font_size_calculated = false;
-
- if (!isset(self::$_defaults)) {
-
- // Shorthand
- $d =& self::$_defaults;
-
- // All CSS 2.1 properties, and their default values
- $d["azimuth"] = "center";
- $d["background_attachment"] = "scroll";
- $d["background_color"] = "transparent";
- $d["background_image"] = "none";
- $d["background_image_resolution"] = "normal";
- $d["_dompdf_background_image_resolution"] = $d["background_image_resolution"];
- $d["background_position"] = "0% 0%";
- $d["background_repeat"] = "repeat";
- $d["background"] = "";
- $d["border_collapse"] = "separate";
- $d["border_color"] = "";
- $d["border_spacing"] = "0";
- $d["border_style"] = "";
- $d["border_top"] = "";
- $d["border_right"] = "";
- $d["border_bottom"] = "";
- $d["border_left"] = "";
- $d["border_top_color"] = "";
- $d["border_right_color"] = "";
- $d["border_bottom_color"] = "";
- $d["border_left_color"] = "";
- $d["border_top_style"] = "none";
- $d["border_right_style"] = "none";
- $d["border_bottom_style"] = "none";
- $d["border_left_style"] = "none";
- $d["border_top_width"] = "medium";
- $d["border_right_width"] = "medium";
- $d["border_bottom_width"] = "medium";
- $d["border_left_width"] = "medium";
- $d["border_width"] = "medium";
- $d["border_bottom_left_radius"] = "";
- $d["border_bottom_right_radius"] = "";
- $d["border_top_left_radius"] = "";
- $d["border_top_right_radius"] = "";
- $d["border_radius"] = "";
- $d["border"] = "";
- $d["bottom"] = "auto";
- $d["caption_side"] = "top";
- $d["clear"] = "none";
- $d["clip"] = "auto";
- $d["color"] = "#000000";
- $d["content"] = "normal";
- $d["counter_increment"] = "none";
- $d["counter_reset"] = "none";
- $d["cue_after"] = "none";
- $d["cue_before"] = "none";
- $d["cue"] = "";
- $d["cursor"] = "auto";
- $d["direction"] = "ltr";
- $d["display"] = "inline";
- $d["elevation"] = "level";
- $d["empty_cells"] = "show";
- $d["float"] = "none";
- $d["font_family"] = $stylesheet->get_dompdf()->getOptions()->getDefaultFont();
- $d["font_size"] = "medium";
- $d["font_style"] = "normal";
- $d["font_variant"] = "normal";
- $d["font_weight"] = "normal";
- $d["font"] = "";
- $d["height"] = "auto";
- $d["image_resolution"] = "normal";
- $d["_dompdf_image_resolution"] = $d["image_resolution"];
- $d["_dompdf_keep"] = "";
- $d["left"] = "auto";
- $d["letter_spacing"] = "normal";
- $d["line_height"] = "normal";
- $d["list_style_image"] = "none";
- $d["list_style_position"] = "outside";
- $d["list_style_type"] = "disc";
- $d["list_style"] = "";
- $d["margin_right"] = "0";
- $d["margin_left"] = "0";
- $d["margin_top"] = "0";
- $d["margin_bottom"] = "0";
- $d["margin"] = "";
- $d["max_height"] = "none";
- $d["max_width"] = "none";
- $d["min_height"] = "0";
- $d["min_width"] = "0";
- $d["opacity"] = "1.0"; // CSS3
- $d["orphans"] = "2";
- $d["outline_color"] = ""; // "invert" special color is not supported
- $d["outline_style"] = "none";
- $d["outline_width"] = "medium";
- $d["outline"] = "";
- $d["overflow"] = "visible";
- $d["padding_top"] = "0";
- $d["padding_right"] = "0";
- $d["padding_bottom"] = "0";
- $d["padding_left"] = "0";
- $d["padding"] = "";
- $d["page_break_after"] = "auto";
- $d["page_break_before"] = "auto";
- $d["page_break_inside"] = "auto";
- $d["pause_after"] = "0";
- $d["pause_before"] = "0";
- $d["pause"] = "";
- $d["pitch_range"] = "50";
- $d["pitch"] = "medium";
- $d["play_during"] = "auto";
- $d["position"] = "static";
- $d["quotes"] = "";
- $d["richness"] = "50";
- $d["right"] = "auto";
- $d["size"] = "auto"; // @page
- $d["speak_header"] = "once";
- $d["speak_numeral"] = "continuous";
- $d["speak_punctuation"] = "none";
- $d["speak"] = "normal";
- $d["speech_rate"] = "medium";
- $d["stress"] = "50";
- $d["table_layout"] = "auto";
- $d["text_align"] = "left";
- $d["text_decoration"] = "none";
- $d["text_indent"] = "0";
- $d["text_transform"] = "none";
- $d["top"] = "auto";
- $d["transform"] = "none"; // CSS3
- $d["transform_origin"] = "50% 50%"; // CSS3
- $d["_webkit_transform"] = $d["transform"]; // CSS3
- $d["_webkit_transform_origin"] = $d["transform_origin"]; // CSS3
- $d["unicode_bidi"] = "normal";
- $d["vertical_align"] = "baseline";
- $d["visibility"] = "visible";
- $d["voice_family"] = "";
- $d["volume"] = "medium";
- $d["white_space"] = "normal";
- $d["word_wrap"] = "normal";
- $d["widows"] = "2";
- $d["width"] = "auto";
- $d["word_spacing"] = "normal";
- $d["z_index"] = "auto";
-
- // for @font-face
- $d["src"] = "";
- $d["unicode_range"] = "";
-
- // Properties that inherit by default
- self::$_inherited = array(
- "azimuth",
- "background_image_resolution",
- "border_collapse",
- "border_spacing",
- "caption_side",
- "color",
- "cursor",
- "direction",
- "elevation",
- "empty_cells",
- "font_family",
- "font_size",
- "font_style",
- "font_variant",
- "font_weight",
- "font",
- "image_resolution",
- "letter_spacing",
- "line_height",
- "list_style_image",
- "list_style_position",
- "list_style_type",
- "list_style",
- "orphans",
- "page_break_inside",
- "pitch_range",
- "pitch",
- "quotes",
- "richness",
- "speak_header",
- "speak_numeral",
- "speak_punctuation",
- "speak",
- "speech_rate",
- "stress",
- "text_align",
- "text_indent",
- "text_transform",
- "visibility",
- "voice_family",
- "volume",
- "white_space",
- "word_wrap",
- "widows",
- "word_spacing",
- );
- }
- }
-
- /**
- * "Destructor": forcibly free all references held by this object
- */
- function dispose()
- {
- }
-
- /**
- * @param $media_queries
- */
- function set_media_queries($media_queries)
- {
- $this->_media_queries = $media_queries;
- }
-
- /**
- * @return array|int
- */
- function get_media_queries()
- {
- return $this->_media_queries;
- }
-
- /**
- * @param Frame $frame
- */
- function set_frame(Frame $frame)
- {
- $this->_frame = $frame;
- }
-
- /**
- * @return Frame
- */
- function get_frame()
- {
- return $this->_frame;
- }
-
- /**
- * @param $origin
- */
- function set_origin($origin)
- {
- $this->_origin = $origin;
- }
-
- /**
- * @return int
- */
- function get_origin()
- {
- return $this->_origin;
- }
-
- /**
- * returns the {@link Stylesheet} this Style is associated with.
- *
- * @return Stylesheet
- */
- function get_stylesheet()
- {
- return $this->_stylesheet;
- }
-
- /**
- * Converts any CSS length value into an absolute length in points.
- *
- * length_in_pt() takes a single length (e.g. '1em') or an array of
- * lengths and returns an absolute length. If an array is passed, then
- * the return value is the sum of all elements. If any of the lengths
- * provided are "auto" or "none" then that value is returned.
- *
- * If a reference size is not provided, the default font size is used
- * ({@link Style::$default_font_size}).
- *
- * @param float|string|array $length the numeric length (or string measurement) or array of lengths to resolve
- * @param float $ref_size an absolute reference size to resolve percentage lengths
- * @return float|string
- */
- function length_in_pt($length, $ref_size = null)
- {
- static $cache = array();
-
- if (!isset($ref_size)) {
- $ref_size = self::$default_font_size;
- }
-
- if (!is_array($length)) {
- $key = $length . "/$ref_size";
- //Early check on cache, before converting $length to array
- if (isset($cache[$key])) {
- return $cache[$key];
- }
- $length = array($length);
- } else {
- $key = implode("@", $length) . "/$ref_size";
- if (isset($cache[$key])) {
- return $cache[$key];
- }
- }
-
- $ret = 0;
- foreach ($length as $l) {
-
- if ($l === "auto") {
- return "auto";
- }
-
- if ($l === "none") {
- return "none";
- }
-
- // Assume numeric values are already in points
- if (is_numeric($l)) {
- $ret += $l;
- continue;
- }
-
- if ($l === "normal") {
- $ret += (float)$ref_size;
- continue;
- }
-
- // Border lengths
- if ($l === "thin") {
- $ret += 0.5;
- continue;
- }
-
- if ($l === "medium") {
- $ret += 1.5;
- continue;
- }
-
- if ($l === "thick") {
- $ret += 2.5;
- continue;
- }
-
- if (($i = mb_strpos($l, "px")) !== false) {
- $dpi = $this->_stylesheet->get_dompdf()->getOptions()->getDpi();
- $ret += ((float)mb_substr($l, 0, $i) * 72) / $dpi;
- continue;
- }
-
- if (($i = mb_strpos($l, "pt")) !== false) {
- $ret += (float)mb_substr($l, 0, $i);
- continue;
- }
-
- if (($i = mb_strpos($l, "%")) !== false) {
- $ret += (float)mb_substr($l, 0, $i) / 100 * (float)$ref_size;
- continue;
- }
-
- if (($i = mb_strpos($l, "rem")) !== false) {
- if ($this->_stylesheet->get_dompdf()->getTree()->get_root()->get_style() === null) {
- // Interpreting it as "em", see https://github.com/dompdf/dompdf/issues/1406
- $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size");
- } else {
- $ret += (float)mb_substr($l, 0, $i) * $this->_stylesheet->get_dompdf()->getTree()->get_root()->get_style()->font_size;
- }
- continue;
- }
-
- if (($i = mb_strpos($l, "em")) !== false) {
- $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size");
- continue;
- }
-
- if (($i = mb_strpos($l, "cm")) !== false) {
- $ret += (float)mb_substr($l, 0, $i) * 72 / 2.54;
- continue;
- }
-
- if (($i = mb_strpos($l, "mm")) !== false) {
- $ret += (float)mb_substr($l, 0, $i) * 72 / 25.4;
- continue;
- }
-
- // FIXME: em:ex ratio?
- if (($i = mb_strpos($l, "ex")) !== false) {
- $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size") / 2;
- continue;
- }
-
- if (($i = mb_strpos($l, "in")) !== false) {
- $ret += (float)mb_substr($l, 0, $i) * 72;
- continue;
- }
-
- if (($i = mb_strpos($l, "pc")) !== false) {
- $ret += (float)mb_substr($l, 0, $i) * 12;
- continue;
- }
-
- // Bogus value
- $ret += (float)$ref_size;
- }
-
- return $cache[$key] = $ret;
- }
-
-
- /**
- * Set inherited properties in this style using values in $parent
- *
- * @param Style $parent
- *
- * @return Style
- */
- function inherit(Style $parent)
- {
-
- // Set parent font size
- $this->_parent_font_size = $parent->get_font_size();
-
- foreach (self::$_inherited as $prop) {
- //inherit the !important property also.
- //if local property is also !important, don't inherit.
- if (isset($parent->_props[$prop]) &&
- (!isset($this->_props[$prop]) ||
- (isset($parent->_important_props[$prop]) && !isset($this->_important_props[$prop]))
- )
- ) {
- if (isset($parent->_important_props[$prop])) {
- $this->_important_props[$prop] = true;
- }
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache[$prop] = null;
- $this->_props[$prop] = $parent->_props[$prop];
- }
- }
-
- foreach ($this->_props as $prop => $value) {
- if ($value === "inherit") {
- if (isset($parent->_important_props[$prop])) {
- $this->_important_props[$prop] = true;
- }
- //do not assign direct, but
- //implicite assignment through __set, redirect to specialized, get value with __get
- //This is for computing defaults if the parent setting is also missing.
- //Therefore do not directly assign the value without __set
- //set _important_props before that to be able to propagate.
- //see __set and __get, on all assignments clear cache!
- //$this->_prop_cache[$prop] = null;
- //$this->_props[$prop] = $parent->_props[$prop];
- //props_set for more obvious explicite assignment not implemented, because
- //too many implicite uses.
- // $this->props_set($prop, $parent->$prop);
- $this->__set($prop, $parent->__get($prop));
- }
- }
-
- return $this;
- }
-
- /**
- * Override properties in this style with those in $style
- *
- * @param Style $style
- */
- function merge(Style $style)
- {
- $shorthand_properties = array("background", "border", "border_bottom", "border_color", "border_left", "border_radius", "border_right", "border_style", "border_top", "border_width", "flex", "font", "list_style", "margin", "padding", "transform");
- //treat the !important attribute
- //if old rule has !important attribute, override with new rule only if
- //the new rule is also !important
- foreach ($style->_props as $prop => $val) {
- $can_merge = false;
- if (isset($style->_important_props[$prop])) {
- $this->_important_props[$prop] = true;
- $can_merge = true;
- } else if (!isset($this->_important_props[$prop])) {
- $can_merge = true;
- }
-
- if ($can_merge) {
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache[$prop] = null;
- $this->_props[$prop] = $val;
-
- // Clear out "inherit" shorthand properties if specific properties have been set
- $shorthands = array_filter($shorthand_properties, function($el) use ($prop) {
- return ( strpos($prop, $el."_") !== false );
- });
- foreach ($shorthands as $shorthand) {
- if (array_key_exists($shorthand, $this->_props) && $this->_props[$shorthand] === "inherit") {
- unset($this->_props[$shorthand]);
- }
- }
- }
- }
-
- if (isset($style->_props["font_size"])) {
- $this->__font_size_calculated = false;
- }
- }
-
- /**
- * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "hex"=>"#rrggbb")
- * based on the provided CSS color value.
- *
- * @param string $color
- * @return array
- */
- function munge_color($color)
- {
- return Color::parse($color);
- }
-
- /* direct access to _important_props array from outside would work only when declared as
- * 'var $_important_props;' instead of 'protected $_important_props;'
- * Don't call _set/__get on missing attribute. Therefore need a special access.
- * Assume that __set will be also called when this is called, so do not check validity again.
- * Only created, if !important exists -> always set true.
- */
- function important_set($prop)
- {
- $prop = str_replace("-", "_", $prop);
- $this->_important_props[$prop] = true;
- }
-
- /**
- * @param $prop
- * @return bool
- */
- function important_get($prop)
- {
- return isset($this->_important_props[$prop]);
- }
-
- /**
- * PHP5 overloaded setter
- *
- * This function along with {@link Style::__get()} permit a user of the
- * Style class to access any (CSS) property using the following syntax:
- *
- * Style->margin_top = "1em";
- * echo (Style->margin_top);
- *
- *
- * __set() automatically calls the provided set function, if one exists,
- * otherwise it sets the property directly. Typically, __set() is not
- * called directly from outside of this class.
- *
- * On each modification clear cache to return accurate setting.
- * Also affects direct settings not using __set
- * For easier finding all assignments, attempted to allowing only explicite assignment:
- * Very many uses, e.g. AbstractFrameReflower.php -> for now leave as it is
- * function __set($prop, $val) {
- * throw new Exception("Implicit replacement of assignment by __set. Not good.");
- * }
- * function props_set($prop, $val) { ... }
- *
- * @param string $prop the property to set
- * @param mixed $val the value of the property
- *
- */
- function __set($prop, $val)
- {
- $prop = str_replace("-", "_", $prop);
- $this->_prop_cache[$prop] = null;
-
- if (!isset(self::$_defaults[$prop])) {
- global $_dompdf_warnings;
- $_dompdf_warnings[] = "'$prop' is not a valid CSS2 property.";
- return;
- }
-
- if ($prop !== "content" && is_string($val) && strlen($val) > 5 && mb_strpos($val, "url") === false) {
- $val = mb_strtolower(trim(str_replace(array("\n", "\t"), array(" "), $val)));
- $val = preg_replace("/([0-9]+) (pt|px|pc|em|ex|in|cm|mm|%)/S", "\\1\\2", $val);
- }
-
- $method = "set_$prop";
-
- if (!isset(self::$_methods_cache[$method])) {
- self::$_methods_cache[$method] = method_exists($this, $method);
- }
-
- if (self::$_methods_cache[$method]) {
- $this->$method($val);
- } else {
- $this->_props[$prop] = $val;
- }
- }
-
- /**
- * PHP5 overloaded getter
- * Along with {@link Style::__set()} __get() provides access to all CSS
- * properties directly. Typically __get() is not called directly outside
- * of this class.
- * On each modification clear cache to return accurate setting.
- * Also affects direct settings not using __set
- *
- * @param string $prop
- *
- * @throws Exception
- * @return mixed
- */
- function __get($prop)
- {
- if (!isset(self::$_defaults[$prop])) {
- throw new Exception("'$prop' is not a valid CSS2 property.");
- }
-
- if (isset($this->_prop_cache[$prop]) && $this->_prop_cache[$prop] != null) {
- return $this->_prop_cache[$prop];
- }
-
- $method = "get_$prop";
-
- // Fall back on defaults if property is not set
- if (!isset($this->_props[$prop])) {
- $this->_props[$prop] = self::$_defaults[$prop];
- }
-
- if (!isset(self::$_methods_cache[$method])) {
- self::$_methods_cache[$method] = method_exists($this, $method);
- }
-
- if (self::$_methods_cache[$method]) {
- return $this->_prop_cache[$prop] = $this->$method();
- }
-
- return $this->_prop_cache[$prop] = $this->_props[$prop];
- }
-
- /**
- * Similar to __get() without storing the result. Useful for accessing
- * properties while loading stylesheets.
- *
- * @param $prop
- * @return string
- * @throws Exception
- */
- function get_prop($prop)
- {
- if (!isset(self::$_defaults[$prop])) {
- throw new Exception("'$prop' is not a valid CSS2 property.");
- }
-
- $method = "get_$prop";
-
- // Fall back on defaults if property is not set
- if (!isset($this->_props[$prop])) {
- return self::$_defaults[$prop];
- }
-
- if (method_exists($this, $method)) {
- return $this->$method();
- }
-
- return $this->_props[$prop];
- }
-
- /**
- * @return float|null|string
- */
- function computed_bottom_spacing() {
- if ($this->_computed_bottom_spacing !== null) {
- return $this->_computed_bottom_spacing;
- }
- return $this->_computed_bottom_spacing = $this->length_in_pt(
- array(
- $this->margin_bottom,
- $this->padding_bottom,
- $this->border_bottom_width
- )
- );
- }
-
- /**
- * @return string
- */
- function get_font_family_raw()
- {
- return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
- }
-
- /**
- * Getter for the 'font-family' CSS property.
- * Uses the {@link FontMetrics} class to resolve the font family into an
- * actual font file.
- *
- * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
- * @throws Exception
- *
- * @return string
- */
- function get_font_family()
- {
- if (isset($this->_font_family)) {
- return $this->_font_family;
- }
-
- $DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss();
-
- // Select the appropriate font. First determine the subtype, then check
- // the specified font-families for a candidate.
-
- // Resolve font-weight
- $weight = $this->__get("font_weight");
-
- if (is_numeric($weight)) {
- if ($weight < 600) {
- $weight = "normal";
- } else {
- $weight = "bold";
- }
- } else if ($weight === "bold" || $weight === "bolder") {
- $weight = "bold";
- } else {
- $weight = "normal";
- }
-
- // Resolve font-style
- $font_style = $this->__get("font_style");
-
- if ($weight === "bold" && ($font_style === "italic" || $font_style === "oblique")) {
- $subtype = "bold_italic";
- } else if ($weight === "bold" && $font_style !== "italic" && $font_style !== "oblique") {
- $subtype = "bold";
- } else if ($weight !== "bold" && ($font_style === "italic" || $font_style === "oblique")) {
- $subtype = "italic";
- } else {
- $subtype = "normal";
- }
-
- // Resolve the font family
- if ($DEBUGCSS) {
- print "[get_font_family:";
- print '(' . $this->_props["font_family"] . '.' . $font_style . '.' . $this->__get("font_weight") . '.' . $weight . '.' . $subtype . ')';
- }
-
- $families = preg_split("/\s*,\s*/", $this->_props["font_family"]);
-
- $font = null;
- foreach ($families as $family) {
- //remove leading and trailing string delimiters, e.g. on font names with spaces;
- //remove leading and trailing whitespace
- $family = trim($family, " \t\n\r\x0B\"'");
- if ($DEBUGCSS) {
- print '(' . $family . ')';
- }
- $font = $this->getFontMetrics()->getFont($family, $subtype);
-
- if ($font) {
- if ($DEBUGCSS) {
- print '(' . $font . ")get_font_family]\n ";
- }
- return $this->_font_family = $font;
- }
- }
-
- $family = null;
- if ($DEBUGCSS) {
- print '(default)';
- }
- $font = $this->getFontMetrics()->getFont($family, $subtype);
-
- if ($font) {
- if ($DEBUGCSS) {
- print '(' . $font . ")get_font_family]\n ";
- }
- return $this->_font_family = $font;
- }
-
- throw new Exception("Unable to find a suitable font replacement for: '" . $this->_props["font_family"] . "'");
-
- }
-
- /**
- * Returns the resolved font size, in points
- *
- * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
- * @return float
- */
- function get_font_size()
- {
-
- if ($this->__font_size_calculated) {
- return $this->_props["font_size"];
- }
-
- if (!isset($this->_props["font_size"])) {
- $fs = self::$_defaults["font_size"];
- } else {
- $fs = $this->_props["font_size"];
- }
-
- if (!isset($this->_parent_font_size)) {
- $this->_parent_font_size = self::$default_font_size;
- }
-
- switch ((string)$fs) {
- case "xx-small":
- case "x-small":
- case "small":
- case "medium":
- case "large":
- case "x-large":
- case "xx-large":
- $fs = self::$default_font_size * self::$font_size_keywords[$fs];
- break;
-
- case "smaller":
- $fs = 8 / 9 * $this->_parent_font_size;
- break;
-
- case "larger":
- $fs = 6 / 5 * $this->_parent_font_size;
- break;
-
- default:
- break;
- }
-
- // Ensure relative sizes resolve to something
- if (($i = mb_strpos($fs, "em")) !== false) {
- $fs = (float)mb_substr($fs, 0, $i) * $this->_parent_font_size;
- } else if (($i = mb_strpos($fs, "ex")) !== false) {
- $fs = (float)mb_substr($fs, 0, $i) * $this->_parent_font_size;
- } else {
- $fs = (float)$this->length_in_pt($fs);
- }
-
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache["font_size"] = null;
- $this->_props["font_size"] = $fs;
- $this->__font_size_calculated = true;
- return $this->_props["font_size"];
-
- }
-
- /**
- * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
- * @return float
- */
- function get_word_spacing()
- {
- if ($this->_props["word_spacing"] === "normal") {
- return 0;
- }
-
- return $this->_props["word_spacing"];
- }
-
- /**
- * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
- * @return float
- */
- function get_letter_spacing()
- {
- if ($this->_props["letter_spacing"] === "normal") {
- return 0;
- }
-
- return $this->_props["letter_spacing"];
- }
-
- /**
- * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
- * @return float
- */
- function get_line_height()
- {
- if (array_key_exists("line_height", $this->_props) === false) {
- $this->_props["line_height"] = self::$_defaults["line_height"];
- }
- $line_height = $this->_props["line_height"];
-
- if ($line_height === "normal") {
- return self::$default_line_height * $this->get_font_size();
- }
-
- if (is_numeric($line_height)) {
- return $this->length_in_pt($line_height . "em", $this->get_font_size());
- }
-
- return $this->length_in_pt($line_height, $this->_parent_font_size);
- }
-
- /**
- * Returns the color as an array
- *
- * The array has the following format:
- * array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb")
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
- * @return array
- */
- function get_color()
- {
- return $this->munge_color($this->_props["color"]);
- }
-
- /**
- * Returns the background color as an array
- *
- * The returned array has the same format as {@link Style::get_color()}
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
- * @return array
- */
- function get_background_color()
- {
- return $this->munge_color($this->_props["background_color"]);
- }
-
- /**
- * Returns the background position as an array
- *
- * The returned array has the following format:
- * array(x,y, "x" => x, "y" => y)
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
- * @return array
- */
- function get_background_position()
- {
- $tmp = explode(" ", $this->_props["background_position"]);
-
- switch ($tmp[0]) {
- case "left":
- $x = "0%";
- break;
-
- case "right":
- $x = "100%";
- break;
-
- case "top":
- $y = "0%";
- break;
-
- case "bottom":
- $y = "100%";
- break;
-
- case "center":
- $x = "50%";
- $y = "50%";
- break;
-
- default:
- $x = $tmp[0];
- break;
- }
-
- if (isset($tmp[1])) {
- switch ($tmp[1]) {
- case "left":
- $x = "0%";
- break;
-
- case "right":
- $x = "100%";
- break;
-
- case "top":
- $y = "0%";
- break;
-
- case "bottom":
- $y = "100%";
- break;
-
- case "center":
- if ($tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center") {
- $y = "50%";
- } else {
- $x = "50%";
- }
- break;
-
- default:
- $y = $tmp[1];
- break;
- }
- } else {
- $y = "50%";
- }
-
- if (!isset($x)) {
- $x = "0%";
- }
-
- if (!isset($y)) {
- $y = "0%";
- }
-
- return array(
- 0 => $x, "x" => $x,
- 1 => $y, "y" => $y,
- );
- }
-
-
- /**
- * Returns the background as it is currently stored
- *
- * (currently anyway only for completeness.
- * not used for further processing)
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
- * @return string
- */
- function get_background_attachment()
- {
- return $this->_props["background_attachment"];
- }
-
-
- /**
- * Returns the background_repeat as it is currently stored
- *
- * (currently anyway only for completeness.
- * not used for further processing)
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
- * @return string
- */
- function get_background_repeat()
- {
- return $this->_props["background_repeat"];
- }
-
-
- /**
- * Returns the background as it is currently stored
- *
- * (currently anyway only for completeness.
- * not used for further processing, but the individual get_background_xxx)
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
- * @return string
- */
- function get_background()
- {
- return $this->_props["background"];
- }
-
-
- /**#@+
- * Returns the border color as an array
- *
- * See {@link Style::get_color()}
- *
- * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
- * @return array
- */
- function get_border_top_color()
- {
- if ($this->_props["border_top_color"] === "") {
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache["border_top_color"] = null;
- $this->_props["border_top_color"] = $this->__get("color");
- }
-
- return $this->munge_color($this->_props["border_top_color"]);
- }
-
- /**
- * @return array
- */
- function get_border_right_color()
- {
- if ($this->_props["border_right_color"] === "") {
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache["border_right_color"] = null;
- $this->_props["border_right_color"] = $this->__get("color");
- }
-
- return $this->munge_color($this->_props["border_right_color"]);
- }
-
- /**
- * @return array
- */
- function get_border_bottom_color()
- {
- if ($this->_props["border_bottom_color"] === "") {
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache["border_bottom_color"] = null;
- $this->_props["border_bottom_color"] = $this->__get("color");
- }
-
- return $this->munge_color($this->_props["border_bottom_color"]);
- }
-
- /**
- * @return array
- */
- function get_border_left_color()
- {
- if ($this->_props["border_left_color"] === "") {
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache["border_left_color"] = null;
- $this->_props["border_left_color"] = $this->__get("color");
- }
-
- return $this->munge_color($this->_props["border_left_color"]);
- }
-
- /**#@-*/
-
- /**#@+
- * Returns the border width, as it is currently stored
- *
- * @link http://www.w3.org/TR/CSS21/box.html#border-width-properties
- * @return float|string
- */
- function get_border_top_width()
- {
- $style = $this->__get("border_top_style");
- return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_top_width"]) : 0;
- }
-
- /**
- * @return float|int|string
- */
- function get_border_right_width()
- {
- $style = $this->__get("border_right_style");
- return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_right_width"]) : 0;
- }
-
- /**
- * @return float|int|string
- */
- function get_border_bottom_width()
- {
- $style = $this->__get("border_bottom_style");
- return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_bottom_width"]) : 0;
- }
-
- /**
- * @return float|int|string
- */
- function get_border_left_width()
- {
- $style = $this->__get("border_left_style");
- return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_left_width"]) : 0;
- }
- /**#@-*/
-
- /**
- * Return an array of all border properties.
- *
- * The returned array has the following structure:
- *
- * array("top" => array("width" => [border-width],
- * "style" => [border-style],
- * "color" => [border-color (array)]),
- * "bottom" ... )
- *
- *
- * @return array
- */
- function get_border_properties()
- {
- return array(
- "top" => array(
- "width" => $this->__get("border_top_width"),
- "style" => $this->__get("border_top_style"),
- "color" => $this->__get("border_top_color"),
- ),
- "bottom" => array(
- "width" => $this->__get("border_bottom_width"),
- "style" => $this->__get("border_bottom_style"),
- "color" => $this->__get("border_bottom_color"),
- ),
- "right" => array(
- "width" => $this->__get("border_right_width"),
- "style" => $this->__get("border_right_style"),
- "color" => $this->__get("border_right_color"),
- ),
- "left" => array(
- "width" => $this->__get("border_left_width"),
- "style" => $this->__get("border_left_style"),
- "color" => $this->__get("border_left_color"),
- ),
- );
- }
-
- /**
- * Return a single border property
- *
- * @param string $side
- *
- * @return mixed
- */
- protected function _get_border($side)
- {
- $color = $this->__get("border_" . $side . "_color");
-
- return $this->__get("border_" . $side . "_width") . " " .
- $this->__get("border_" . $side . "_style") . " " . $color["hex"];
- }
-
- /**#@+
- * Return full border properties as a string
- *
- * Border properties are returned just as specified in CSS:
- * [width] [style] [color]
- * e.g. "1px solid blue"
- *
- * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
- * @return string
- */
- function get_border_top()
- {
- return $this->_get_border("top");
- }
-
- /**
- * @return mixed
- */
- function get_border_right()
- {
- return $this->_get_border("right");
- }
-
- /**
- * @return mixed
- */
- function get_border_bottom()
- {
- return $this->_get_border("bottom");
- }
-
- /**
- * @return mixed
- */
- function get_border_left()
- {
- return $this->_get_border("left");
- }
-
- /**
- * @param $w
- * @param $h
- * @return array|null
- */
- function get_computed_border_radius($w, $h)
- {
- if (!empty($this->_computed_border_radius)) {
- return $this->_computed_border_radius;
- }
-
- $w = (float)$w;
- $h = (float)$h;
- $rTL = (float)$this->__get("border_top_left_radius");
- $rTR = (float)$this->__get("border_top_right_radius");
- $rBL = (float)$this->__get("border_bottom_left_radius");
- $rBR = (float)$this->__get("border_bottom_right_radius");
-
- if ($rTL + $rTR + $rBL + $rBR == 0) {
- return $this->_computed_border_radius = array(
- 0, 0, 0, 0,
- "top-left" => 0,
- "top-right" => 0,
- "bottom-right" => 0,
- "bottom-left" => 0,
- );
- }
-
- $t = (float)$this->__get("border_top_width");
- $r = (float)$this->__get("border_right_width");
- $b = (float)$this->__get("border_bottom_width");
- $l = (float)$this->__get("border_left_width");
-
- $rTL = min($rTL, $h - $rBL - $t / 2 - $b / 2, $w - $rTR - $l / 2 - $r / 2);
- $rTR = min($rTR, $h - $rBR - $t / 2 - $b / 2, $w - $rTL - $l / 2 - $r / 2);
- $rBL = min($rBL, $h - $rTL - $t / 2 - $b / 2, $w - $rBR - $l / 2 - $r / 2);
- $rBR = min($rBR, $h - $rTR - $t / 2 - $b / 2, $w - $rBL - $l / 2 - $r / 2);
-
- return $this->_computed_border_radius = array(
- $rTL, $rTR, $rBR, $rBL,
- "top-left" => $rTL,
- "top-right" => $rTR,
- "bottom-right" => $rBR,
- "bottom-left" => $rBL,
- );
- }
-
- /**
- * Returns the outline color as an array
- *
- * See {@link Style::get_color()}
- *
- * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
- * @return array
- */
- function get_outline_color()
- {
- if ($this->_props["outline_color"] === "") {
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache["outline_color"] = null;
- $this->_props["outline_color"] = $this->__get("color");
- }
-
- return $this->munge_color($this->_props["outline_color"]);
- }
-
- /**#@+
- * Returns the outline width, as it is currently stored
- * @return float|string
- */
- function get_outline_width()
- {
- $style = $this->__get("outline_style");
- return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["outline_width"]) : 0;
- }
-
- /**#@+
- * Return full outline properties as a string
- *
- * Outline properties are returned just as specified in CSS:
- * [width] [style] [color]
- * e.g. "1px solid blue"
- *
- * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
- * @return string
- */
- function get_outline()
- {
- $color = $this->__get("outline_color");
- return
- $this->__get("outline_width") . " " .
- $this->__get("outline_style") . " " .
- $color["hex"];
- }
- /**#@-*/
-
- /**
- * Returns border spacing as an array
- *
- * The array has the format (h_space,v_space)
- *
- * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
- * @return array
- */
- function get_border_spacing()
- {
- $arr = explode(" ", $this->_props["border_spacing"]);
- if (count($arr) == 1) {
- $arr[1] = $arr[0];
- }
- return $arr;
- }
-
- /*==============================*/
-
- /*
- !important attribute
- For basic functionality of the !important attribute with overloading
- of several styles of an element, changes in inherit(), merge() and _parse_properties()
- are sufficient [helpers var $_important_props, __construct(), important_set(), important_get()]
-
- Only for combined attributes extra treatment needed. See below.
-
- div { border: 1px red; }
- div { border: solid; } // Not combined! Only one occurrence of same style per context
- //
- div { border: 1px red; }
- div a { border: solid; } // Adding to border style ok by inheritance
- //
- div { border-style: solid; } // Adding to border style ok because of different styles
- div { border: 1px red; }
- //
- div { border-style: solid; !important} // border: overrides, even though not !important
- div { border: 1px dashed red; }
- //
- div { border: 1px red; !important }
- div a { border-style: solid; } // Need to override because not set
-
- Special treatment:
- At individual property like border-top-width need to check whether overriding value is also !important.
- Also store the !important condition for later overrides.
- Since not known who is initiating the override, need to get passed !important as parameter.
- !important Parameter taken as in the original style in the css file.
- When property border !important given, do not mark subsets like border_style as important. Only
- individual properties.
-
- Note:
- Setting individual property directly from css with e.g. set_border_top_style() is not needed, because
- missing set functions handled by a generic handler __set(), including the !important.
- Setting individual property of as sub-property is handled below.
-
- Implementation see at _set_style_side_type()
- Callers _set_style_sides_type(), _set_style_type, _set_style_type_important()
-
- Related functionality for background, padding, margin, font, list_style
- */
-
- /**
- * Generalized set function for individual attribute of combined style.
- * With check for !important
- * Applicable for background, border, padding, margin, font, list_style
- *
- * Note: $type has a leading underscore (or is empty), the others not.
- *
- * @param $style
- * @param $side
- * @param $type
- * @param $val
- * @param $important
- */
- protected function _set_style_side_type($style, $side, $type, $val, $important)
- {
- $prop = $style . '_' . $side . $type;
-
- if (!isset($this->_important_props[$prop]) || $important) {
- if ($side === "bottom") {
- $this->_computed_bottom_spacing = null; //reset computed cache, border style can disable/enable border calculations
- }
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache[$prop] = null;
- if ($important) {
- $this->_important_props[$prop] = true;
- }
- $this->_props[$prop] = $val;
- }
- }
-
- /**
- * @param $style
- * @param $top
- * @param $right
- * @param $bottom
- * @param $left
- * @param $type
- * @param $important
- */
- protected function _set_style_sides_type($style, $top, $right, $bottom, $left, $type, $important)
- {
- $this->_set_style_side_type($style, 'top', $type, $top, $important);
- $this->_set_style_side_type($style, 'right', $type, $right, $important);
- $this->_set_style_side_type($style, 'bottom', $type, $bottom, $important);
- $this->_set_style_side_type($style, 'left', $type, $left, $important);
- }
-
- /**
- * @param $style
- * @param $type
- * @param $val
- * @param $important
- */
- protected function _set_style_type($style, $type, $val, $important)
- {
- $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
- $arr = explode(" ", $val);
-
- switch (count($arr)) {
- case 1:
- $this->_set_style_sides_type($style, $arr[0], $arr[0], $arr[0], $arr[0], $type, $important);
- break;
- case 2:
- $this->_set_style_sides_type($style, $arr[0], $arr[1], $arr[0], $arr[1], $type, $important);
- break;
- case 3:
- $this->_set_style_sides_type($style, $arr[0], $arr[1], $arr[2], $arr[1], $type, $important);
- break;
- case 4:
- $this->_set_style_sides_type($style, $arr[0], $arr[1], $arr[2], $arr[3], $type, $important);
- break;
- }
-
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache[$style . $type] = null;
- $this->_props[$style . $type] = $val;
- }
-
- /**
- * @param $style
- * @param $type
- * @param $val
- */
- protected function _set_style_type_important($style, $type, $val)
- {
- $this->_set_style_type($style, $type, $val, isset($this->_important_props[$style . $type]));
- }
-
- /**
- * Anyway only called if _important matches and is assigned
- * E.g. _set_style_side_type($style,$side,'',str_replace("none", "0px", $val),isset($this->_important_props[$style.'_'.$side]));
- *
- * @param $style
- * @param $side
- * @param $val
- */
- protected function _set_style_side_width_important($style, $side, $val)
- {
- if ($side === "bottom") {
- $this->_computed_bottom_spacing = null; //reset cache for any bottom width changes
- }
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache[$style . '_' . $side] = null;
- $this->_props[$style . '_' . $side] = str_replace("none", "0px", $val);
- }
-
- /**
- * @param $style
- * @param $val
- * @param $important
- */
- protected function _set_style($style, $val, $important)
- {
- if (!isset($this->_important_props[$style]) || $important) {
- if ($important) {
- $this->_important_props[$style] = true;
- }
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache[$style] = null;
- $this->_props[$style] = $val;
- }
- }
-
- /**
- * @param $val
- * @return string
- */
- protected function _image($val)
- {
- $DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss();
- $parsed_url = "none";
-
- if (mb_strpos($val, "url") === false) {
- $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none
- } else {
- $val = preg_replace("/url\(\s*['\"]?([^'\")]+)['\"]?\s*\)/", "\\1", trim($val));
-
- // Resolve the url now in the context of the current stylesheet
- $parsed_url = Helpers::explode_url($val);
- if ($parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "") {
- if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\') {
- $path = $_SERVER["DOCUMENT_ROOT"] . '/';
- } else {
- $path = $this->_stylesheet->get_base_path();
- }
-
- $path .= $parsed_url["path"] . $parsed_url["file"];
- $path = realpath($path);
- // If realpath returns FALSE then specifically state that there is no background image
- if (!$path) {
- $path = 'none';
- }
- } else {
- $path = Helpers::build_url($this->_stylesheet->get_protocol(),
- $this->_stylesheet->get_host(),
- $this->_stylesheet->get_base_path(),
- $val);
- }
- }
- if ($DEBUGCSS) {
- print "[_image\n";
- print_r($parsed_url);
- print $this->_stylesheet->get_protocol() . "\n" . $this->_stylesheet->get_base_path() . "\n" . $path . "\n";
- print "_image] ";;
- }
- return $path;
- }
-
- /*======================*/
-
- /**
- * Sets color
- *
- * The color parameter can be any valid CSS color value
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
- * @param string $color
- */
- function set_color($color)
- {
- $col = $this->munge_color($color);
-
- if (is_null($col) || !isset($col["hex"])) {
- $color = "inherit";
- } else {
- $color = $col["hex"];
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["color"] = null;
- $this->_props["color"] = $color;
- }
-
- /**
- * Sets the background color
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
- * @param string $color
- */
- function set_background_color($color)
- {
- $col = $this->munge_color($color);
-
- if (is_null($col)) {
- return;
- //$col = self::$_defaults["background_color"];
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["background_color"] = null;
- $this->_props["background_color"] = is_array($col) ? $col["hex"] : $col;
- }
-
- /**
- * Set the background image url
- * @link http://www.w3.org/TR/CSS21/colors.html#background-properties
- *
- * @param string $val
- */
- function set_background_image($val)
- {
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["background_image"] = null;
- $this->_props["background_image"] = $this->_image($val);
- }
-
- /**
- * Sets the background repeat
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
- * @param string $val
- */
- function set_background_repeat($val)
- {
- if (is_null($val)) {
- $val = self::$_defaults["background_repeat"];
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["background_repeat"] = null;
- $this->_props["background_repeat"] = $val;
- }
-
- /**
- * Sets the background attachment
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
- * @param string $val
- */
- function set_background_attachment($val)
- {
- if (is_null($val)) {
- $val = self::$_defaults["background_attachment"];
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["background_attachment"] = null;
- $this->_props["background_attachment"] = $val;
- }
-
- /**
- * Sets the background position
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
- * @param string $val
- */
- function set_background_position($val)
- {
- if (is_null($val)) {
- $val = self::$_defaults["background_position"];
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["background_position"] = null;
- $this->_props["background_position"] = $val;
- }
-
- /**
- * Sets the background - combined options
- *
- * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
- * @param string $val
- */
- function set_background($val)
- {
- $val = trim($val);
- $important = isset($this->_important_props["background"]);
-
- if ($val === "none") {
- $this->_set_style("background_image", "none", $important);
- $this->_set_style("background_color", "transparent", $important);
- } else {
- $pos = array();
- $tmp = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
- $tmp = preg_split("/\s+/", $tmp);
-
- foreach ($tmp as $attr) {
- if (mb_substr($attr, 0, 3) === "url" || $attr === "none") {
- $this->_set_style("background_image", $this->_image($attr), $important);
- } elseif ($attr === "fixed" || $attr === "scroll") {
- $this->_set_style("background_attachment", $attr, $important);
- } elseif ($attr === "repeat" || $attr === "repeat-x" || $attr === "repeat-y" || $attr === "no-repeat") {
- $this->_set_style("background_repeat", $attr, $important);
- } elseif (($col = $this->munge_color($attr)) != null) {
- $this->_set_style("background_color", is_array($col) ? $col["hex"] : $col, $important);
- } else {
- $pos[] = $attr;
- }
- }
-
- if (count($pos)) {
- $this->_set_style("background_position", implode(" ", $pos), $important);
- }
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["background"] = null;
- $this->_props["background"] = $val;
- }
-
- /**
- * Sets the font size
- *
- * $size can be any acceptable CSS size
- *
- * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
- * @param string|float $size
- */
- function set_font_size($size)
- {
- $this->__font_size_calculated = false;
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["font_size"] = null;
- $this->_props["font_size"] = $size;
- }
-
- /**
- * Sets the font style
- *
- * combined attributes
- * set individual attributes also, respecting !important mark
- * exactly this order, separate by space. Multiple fonts separated by comma:
- * font-style, font-variant, font-weight, font-size, line-height, font-family
- *
- * Other than with border and list, existing partial attributes should
- * reset when starting here, even when not mentioned.
- * If individual attribute is !important and explicit or implicit replacement is not,
- * keep individual attribute
- *
- * require whitespace as delimiters for single value attributes
- * On delimiter "/" treat first as font height, second as line height
- * treat all remaining at the end of line as font
- * font-style, font-variant, font-weight, font-size, line-height, font-family
- *
- * missing font-size and font-family might be not allowed, but accept it here and
- * use default (medium size, empty font name)
- *
- * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
- * @param $val
- */
- function set_font($val)
- {
- $this->__font_size_calculated = false;
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["font"] = null;
- $this->_props["font"] = $val;
-
- $important = isset($this->_important_props["font"]);
-
- if (preg_match("/^(italic|oblique|normal)\s*(.*)$/i", $val, $match)) {
- $this->_set_style("font_style", $match[1], $important);
- $val = $match[2];
- } else {
- $this->_set_style("font_style", self::$_defaults["font_style"], $important);
- }
-
- if (preg_match("/^(small-caps|normal)\s*(.*)$/i", $val, $match)) {
- $this->_set_style("font_variant", $match[1], $important);
- $val = $match[2];
- } else {
- $this->_set_style("font_variant", self::$_defaults["font_variant"], $important);
- }
-
- //matching numeric value followed by unit -> this is indeed a subsequent font size. Skip!
- if (preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i", $val, $match) &&
- !preg_match("/^(?:pt|px|pc|em|ex|in|cm|mm|%)/", $match[2])
- ) {
- $this->_set_style("font_weight", $match[1], $important);
- $val = $match[2];
- } else {
- $this->_set_style("font_weight", self::$_defaults["font_weight"], $important);
- }
-
- if (preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))(?:\/|\s*)(.*)$/i", $val, $match)) {
- $this->_set_style("font_size", $match[1], $important);
- $val = $match[2];
- if (preg_match("/^(?:\/|\s*)(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%)?)\s*(.*)$/i", $val, $match)) {
- $this->_set_style("line_height", $match[1], $important);
- $val = $match[2];
- } else {
- $this->_set_style("line_height", self::$_defaults["line_height"], $important);
- }
- } else {
- $this->_set_style("font_size", self::$_defaults["font_size"], $important);
- $this->_set_style("line_height", self::$_defaults["line_height"], $important);
- }
-
- if (strlen($val) != 0) {
- $this->_set_style("font_family", $val, $important);
- } else {
- $this->_set_style("font_family", self::$_defaults["font_family"], $important);
- }
- }
-
- /**
- * Sets page break properties
- *
- * @link http://www.w3.org/TR/CSS21/page.html#page-breaks
- * @param string $break
- */
- function set_page_break_before($break)
- {
- if ($break === "left" || $break === "right") {
- $break = "always";
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["page_break_before"] = null;
- $this->_props["page_break_before"] = $break;
- }
-
- /**
- * @param $break
- */
- function set_page_break_after($break)
- {
- if ($break === "left" || $break === "right") {
- $break = "always";
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["page_break_after"] = null;
- $this->_props["page_break_after"] = $break;
- }
-
- /**
- * Sets the margin size
- *
- * @link http://www.w3.org/TR/CSS21/box.html#margin-properties
- * @param $val
- */
- function set_margin_top($val)
- {
- $this->_set_style_side_width_important('margin', 'top', $val);
- }
-
- /**
- * @param $val
- */
- function set_margin_right($val)
- {
- $this->_set_style_side_width_important('margin', 'right', $val);
- }
-
- /**
- * @param $val
- */
- function set_margin_bottom($val)
- {
- $this->_set_style_side_width_important('margin', 'bottom', $val);
- }
-
- /**
- * @param $val
- */
- function set_margin_left($val)
- {
- $this->_set_style_side_width_important('margin', 'left', $val);
- }
-
- /**
- * @param $val
- */
- function set_margin($val)
- {
- $val = str_replace("none", "0px", $val);
- $this->_set_style_type_important('margin', '', $val);
- }
-
- /**
- * Sets the padding size
- *
- * @link http://www.w3.org/TR/CSS21/box.html#padding-properties
- * @param $val
- */
- function set_padding_top($val)
- {
- $this->_set_style_side_width_important('padding', 'top', $val);
- }
-
- /**
- * @param $val
- */
- function set_padding_right($val)
- {
- $this->_set_style_side_width_important('padding', 'right', $val);
- }
-
- /**
- * @param $val
- */
- function set_padding_bottom($val)
- {
- $this->_set_style_side_width_important('padding', 'bottom', $val);
- }
-
- /**
- * @param $val
- */
- function set_padding_left($val)
- {
- $this->_set_style_side_width_important('padding', 'left', $val);
- }
-
- /**
- * @param $val
- */
- function set_padding($val)
- {
- $val = str_replace("none", "0px", $val);
- $this->_set_style_type_important('padding', '', $val);
- }
- /**#@-*/
-
- /**
- * Sets a single border
- *
- * @param string $side
- * @param string $border_spec ([width] [style] [color])
- * @param boolean $important
- */
- protected function _set_border($side, $border_spec, $important)
- {
- $border_spec = preg_replace("/\s*\,\s*/", ",", $border_spec);
- //$border_spec = str_replace(",", " ", $border_spec); // Why did we have this ?? rbg(10, 102, 10) > rgb(10 102 10)
- $arr = explode(" ", $border_spec);
-
- // FIXME: handle partial values
-
- //For consistency of individual and combined properties, and with ie8 and firefox3
- //reset all attributes, even if only partially given
- $this->_set_style_side_type('border', $side, '_style', self::$_defaults['border_' . $side . '_style'], $important);
- $this->_set_style_side_type('border', $side, '_width', self::$_defaults['border_' . $side . '_width'], $important);
- $this->_set_style_side_type('border', $side, '_color', self::$_defaults['border_' . $side . '_color'], $important);
-
- foreach ($arr as $value) {
- $value = trim($value);
- if (in_array($value, self::$BORDER_STYLES)) {
- $this->_set_style_side_type('border', $side, '_style', $value, $important);
- } else if (preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value)) {
- $this->_set_style_side_type('border', $side, '_width', $value, $important);
- } else {
- // must be color
- $this->_set_style_side_type('border', $side, '_color', $value, $important);
- }
- }
-
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache['border_' . $side] = null;
- $this->_props['border_' . $side] = $border_spec;
- }
-
- /**
- * Sets the border styles
- *
- * @link http://www.w3.org/TR/CSS21/box.html#border-properties
- * @param string $val
- */
- function set_border_top($val)
- {
- $this->_set_border("top", $val, isset($this->_important_props['border_top']));
- }
-
- /**
- * @param $val
- */
- function set_border_right($val)
- {
- $this->_set_border("right", $val, isset($this->_important_props['border_right']));
- }
-
- /**
- * @param $val
- */
- function set_border_bottom($val)
- {
- $this->_set_border("bottom", $val, isset($this->_important_props['border_bottom']));
- }
-
- /**
- * @param $val
- */
- function set_border_left($val)
- {
- $this->_set_border("left", $val, isset($this->_important_props['border_left']));
- }
-
- /**
- * @param $val
- */
- function set_border($val)
- {
- $important = isset($this->_important_props["border"]);
- $this->_set_border("top", $val, $important);
- $this->_set_border("right", $val, $important);
- $this->_set_border("bottom", $val, $important);
- $this->_set_border("left", $val, $important);
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["border"] = null;
- $this->_props["border"] = $val;
- }
-
- /**
- * @param $val
- */
- function set_border_width($val)
- {
- $this->_set_style_type_important('border', '_width', $val);
- }
-
- /**
- * @param $val
- */
- function set_border_color($val)
- {
- $this->_set_style_type_important('border', '_color', $val);
- }
-
- /**
- * @param $val
- */
- function set_border_style($val)
- {
- $this->_set_style_type_important('border', '_style', $val);
- }
-
- /**
- * Sets the border radius size
- *
- * http://www.w3.org/TR/css3-background/#corners
- *
- * @param $val
- */
- function set_border_top_left_radius($val)
- {
- $this->_set_border_radius_corner($val, "top_left");
- }
-
- /**
- * @param $val
- */
- function set_border_top_right_radius($val)
- {
- $this->_set_border_radius_corner($val, "top_right");
- }
-
- /**
- * @param $val
- */
- function set_border_bottom_left_radius($val)
- {
- $this->_set_border_radius_corner($val, "bottom_left");
- }
-
- /**
- * @param $val
- */
- function set_border_bottom_right_radius($val)
- {
- $this->_set_border_radius_corner($val, "bottom_right");
- }
-
- /**
- * @param $val
- */
- function set_border_radius($val)
- {
- $val = preg_replace("/\s*\,\s*/", ",", $val); // when border-radius has spaces
- $arr = explode(" ", $val);
-
- switch (count($arr)) {
- case 1:
- $this->_set_border_radii($arr[0], $arr[0], $arr[0], $arr[0]);
- break;
- case 2:
- $this->_set_border_radii($arr[0], $arr[1], $arr[0], $arr[1]);
- break;
- case 3:
- $this->_set_border_radii($arr[0], $arr[1], $arr[2], $arr[1]);
- break;
- case 4:
- $this->_set_border_radii($arr[0], $arr[1], $arr[2], $arr[3]);
- break;
- }
- }
-
- /**
- * @param $val1
- * @param $val2
- * @param $val3
- * @param $val4
- */
- protected function _set_border_radii($val1, $val2, $val3, $val4)
- {
- $this->_set_border_radius_corner($val1, "top_left");
- $this->_set_border_radius_corner($val2, "top_right");
- $this->_set_border_radius_corner($val3, "bottom_right");
- $this->_set_border_radius_corner($val4, "bottom_left");
- }
-
- /**
- * @param $val
- * @param $corner
- */
- protected function _set_border_radius_corner($val, $corner)
- {
- $this->_has_border_radius = true;
-
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache["border_" . $corner . "_radius"] = null;
-
- $this->_props["border_" . $corner . "_radius"] = $val;
- }
-
- /**
- * @return float|int|string
- */
- function get_border_top_left_radius()
- {
- return $this->_get_border_radius_corner("top_left");
- }
-
- /**
- * @return float|int|string
- */
- function get_border_top_right_radius()
- {
- return $this->_get_border_radius_corner("top_right");
- }
-
- /**
- * @return float|int|string
- */
- function get_border_bottom_left_radius()
- {
- return $this->_get_border_radius_corner("bottom_left");
- }
-
- /**
- * @return float|int|string
- */
- function get_border_bottom_right_radius()
- {
- return $this->_get_border_radius_corner("bottom_right");
- }
-
- /**
- * @param $corner
- * @return float|int|string
- */
- protected function _get_border_radius_corner($corner)
- {
- if (!isset($this->_props["border_" . $corner . "_radius"]) || empty($this->_props["border_" . $corner . "_radius"])) {
- return 0;
- }
-
- return $this->length_in_pt($this->_props["border_" . $corner . "_radius"]);
- }
-
- /**
- * Sets the outline styles
- *
- * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines
- * @param string $val
- */
- function set_outline($val)
- {
- $important = isset($this->_important_props["outline"]);
-
- $props = array(
- "outline_style",
- "outline_width",
- "outline_color",
- );
-
- foreach ($props as $prop) {
- $_val = self::$_defaults[$prop];
-
- if (!isset($this->_important_props[$prop]) || $important) {
- //see __set and __get, on all assignments clear cache!
- $this->_prop_cache[$prop] = null;
- if ($important) {
- $this->_important_props[$prop] = true;
- }
- $this->_props[$prop] = $_val;
- }
- }
-
- $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
- $arr = explode(" ", $val);
- foreach ($arr as $value) {
- $value = trim($value);
-
- if (in_array($value, self::$BORDER_STYLES)) {
- $this->set_outline_style($value);
- } else if (preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value)) {
- $this->set_outline_width($value);
- } else {
- // must be color
- $this->set_outline_color($value);
- }
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["outline"] = null;
- $this->_props["outline"] = $val;
- }
-
- /**
- * @param $val
- */
- function set_outline_width($val)
- {
- $this->_set_style_type_important('outline', '_width', $val);
- }
-
- /**
- * @param $val
- */
- function set_outline_color($val)
- {
- $this->_set_style_type_important('outline', '_color', $val);
- }
-
- /**
- * @param $val
- */
- function set_outline_style($val)
- {
- $this->_set_style_type_important('outline', '_style', $val);
- }
-
- /**
- * Sets the border spacing
- *
- * @link http://www.w3.org/TR/CSS21/box.html#border-properties
- * @param float $val
- */
- function set_border_spacing($val)
- {
- $arr = explode(" ", $val);
-
- if (count($arr) == 1) {
- $arr[1] = $arr[0];
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["border_spacing"] = null;
- $this->_props["border_spacing"] = "$arr[0] $arr[1]";
- }
-
- /**
- * Sets the list style image
- *
- * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
- * @param $val
- */
- function set_list_style_image($val)
- {
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["list_style_image"] = null;
- $this->_props["list_style_image"] = $this->_image($val);
- }
-
- /**
- * Sets the list style
- *
- * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
- * @param $val
- */
- function set_list_style($val)
- {
- $important = isset($this->_important_props["list_style"]);
- $arr = explode(" ", str_replace(",", " ", $val));
-
- static $types = array(
- "disc", "circle", "square",
- "decimal-leading-zero", "decimal", "1",
- "lower-roman", "upper-roman", "a", "A",
- "lower-greek",
- "lower-latin", "upper-latin",
- "lower-alpha", "upper-alpha",
- "armenian", "georgian", "hebrew",
- "cjk-ideographic", "hiragana", "katakana",
- "hiragana-iroha", "katakana-iroha", "none"
- );
-
- static $positions = array("inside", "outside");
-
- foreach ($arr as $value) {
- /* http://www.w3.org/TR/CSS21/generate.html#list-style
- * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
- */
- if ($value === "none") {
- $this->_set_style("list_style_type", $value, $important);
- $this->_set_style("list_style_image", $value, $important);
- continue;
- }
-
- //On setting or merging or inheriting list_style_image as well as list_style_type,
- //and url exists, then url has precedence, otherwise fall back to list_style_type
- //Firefox is wrong here (list_style_image gets overwritten on explicit list_style_type)
- //Internet Explorer 7/8 and dompdf is right.
-
- if (mb_substr($value, 0, 3) === "url") {
- $this->_set_style("list_style_image", $this->_image($value), $important);
- continue;
- }
-
- if (in_array($value, $types)) {
- $this->_set_style("list_style_type", $value, $important);
- } else if (in_array($value, $positions)) {
- $this->_set_style("list_style_position", $value, $important);
- }
- }
-
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["list_style"] = null;
- $this->_props["list_style"] = $val;
- }
-
- /**
- * @param $val
- */
- function set_size($val)
- {
- $length_re = "/(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))/";
-
- $val = mb_strtolower($val);
-
- if ($val === "auto") {
- return;
- }
-
- $parts = preg_split("/\s+/", $val);
-
- $computed = array();
- if (preg_match($length_re, $parts[0])) {
- $computed[] = $this->length_in_pt($parts[0]);
-
- if (isset($parts[1]) && preg_match($length_re, $parts[1])) {
- $computed[] = $this->length_in_pt($parts[1]);
- } else {
- $computed[] = $computed[0];
- }
-
- if (isset($parts[2]) && $parts[2] === "landscape") {
- $computed = array_reverse($computed);
- }
- } elseif (isset(CPDF::$PAPER_SIZES[$parts[0]])) {
- $computed = array_slice(CPDF::$PAPER_SIZES[$parts[0]], 2, 2);
-
- if (isset($parts[1]) && $parts[1] === "landscape") {
- $computed = array_reverse($computed);
- }
- } else {
- return;
- }
-
- $this->_props["size"] = $computed;
- }
-
- /**
- * Gets the CSS3 transform property
- *
- * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property
- * @return array|null
- */
- function get_transform()
- {
- $number = "\s*([^,\s]+)\s*";
- $tr_value = "\s*([^,\s]+)\s*";
- $angle = "\s*([^,\s]+(?:deg|rad)?)\s*";
-
- if (!preg_match_all("/[a-z]+\([^\)]+\)/i", $this->_props["transform"], $parts, PREG_SET_ORDER)) {
- return null;
- }
-
- $functions = array(
- //"matrix" => "\($number,$number,$number,$number,$number,$number\)",
-
- "translate" => "\($tr_value(?:,$tr_value)?\)",
- "translateX" => "\($tr_value\)",
- "translateY" => "\($tr_value\)",
-
- "scale" => "\($number(?:,$number)?\)",
- "scaleX" => "\($number\)",
- "scaleY" => "\($number\)",
-
- "rotate" => "\($angle\)",
-
- "skew" => "\($angle(?:,$angle)?\)",
- "skewX" => "\($angle\)",
- "skewY" => "\($angle\)",
- );
-
- $transforms = array();
-
- foreach ($parts as $part) {
- $t = $part[0];
-
- foreach ($functions as $name => $pattern) {
- if (preg_match("/$name\s*$pattern/i", $t, $matches)) {
- $values = array_slice($matches, 1);
-
- switch ($name) {
- // units
- case "rotate":
- case "skew":
- case "skewX":
- case "skewY":
-
- foreach ($values as $i => $value) {
- if (strpos($value, "rad")) {
- $values[$i] = rad2deg(floatval($value));
- } else {
- $values[$i] = floatval($value);
- }
- }
-
- switch ($name) {
- case "skew":
- if (!isset($values[1])) {
- $values[1] = 0;
- }
- break;
- case "skewX":
- $name = "skew";
- $values = array($values[0], 0);
- break;
- case "skewY":
- $name = "skew";
- $values = array(0, $values[0]);
- break;
- }
- break;
-
- // units
- case "translate":
- $values[0] = $this->length_in_pt($values[0], (float)$this->length_in_pt($this->width));
-
- if (isset($values[1])) {
- $values[1] = $this->length_in_pt($values[1], (float)$this->length_in_pt($this->height));
- } else {
- $values[1] = 0;
- }
- break;
-
- case "translateX":
- $name = "translate";
- $values = array($this->length_in_pt($values[0], (float)$this->length_in_pt($this->width)), 0);
- break;
-
- case "translateY":
- $name = "translate";
- $values = array(0, $this->length_in_pt($values[0], (float)$this->length_in_pt($this->height)));
- break;
-
- // units
- case "scale":
- if (!isset($values[1])) {
- $values[1] = $values[0];
- }
- break;
-
- case "scaleX":
- $name = "scale";
- $values = array($values[0], 1.0);
- break;
-
- case "scaleY":
- $name = "scale";
- $values = array(1.0, $values[0]);
- break;
- }
-
- $transforms[] = array(
- $name,
- $values,
- );
- }
- }
- }
-
- return $transforms;
- }
-
- /**
- * @param $val
- */
- function set_transform($val)
- {
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["transform"] = null;
- $this->_props["transform"] = $val;
- }
-
- /**
- * @param $val
- */
- function set__webkit_transform($val)
- {
- $this->set_transform($val);
- }
-
- /**
- * @param $val
- */
- function set__webkit_transform_origin($val)
- {
- $this->set_transform_origin($val);
- }
-
- /**
- * Sets the CSS3 transform-origin property
- *
- * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
- * @param string $val
- */
- function set_transform_origin($val)
- {
- //see __set and __get, on all assignments clear cache, not needed on direct set through __set
- $this->_prop_cache["transform_origin"] = null;
- $this->_props["transform_origin"] = $val;
- }
-
- /**
- * Gets the CSS3 transform-origin property
- *
- * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
- * @return mixed[]
- */
- function get_transform_origin() {
- $values = preg_split("/\s+/", $this->_props['transform_origin']);
-
- if (count($values) === 0) {
- $values = preg_split("/\s+/", self::$_defaults["transform_origin"]);
- }
-
- $values = array_map(function($value) {
- if (in_array($value, array("top", "left"))) {
- return 0;
- } else if (in_array($value, array("bottom", "right"))) {
- return "100%";
- } else {
- return $value;
- }
- }, $values);
-
- if (!isset($values[1])) {
- $values[1] = $values[0];
- }
-
- return $values;
- }
-
- /**
- * @param $val
- * @return null
- */
- protected function parse_image_resolution($val)
- {
- // If exif data could be get:
- // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';
-
- $re = '/^\s*(\d+|normal|auto)\s*$/';
-
- if (!preg_match($re, $val, $matches)) {
- return null;
- }
-
- return $matches[1];
- }
-
- /**
- * auto | normal | dpi
- *
- * @param $val
- */
- function set_background_image_resolution($val)
- {
- $parsed = $this->parse_image_resolution($val);
-
- $this->_prop_cache["background_image_resolution"] = null;
- $this->_props["background_image_resolution"] = $parsed;
- }
-
- /**
- * auto | normal | dpi
- *
- * @param $val
- */
- function set_image_resolution($val)
- {
- $parsed = $this->parse_image_resolution($val);
-
- $this->_prop_cache["image_resolution"] = null;
- $this->_props["image_resolution"] = $parsed;
- }
-
- /**
- * @param $val
- */
- function set__dompdf_background_image_resolution($val)
- {
- $this->set_background_image_resolution($val);
- }
-
- /**
- * @param $val
- */
- function set__dompdf_image_resolution($val)
- {
- $this->set_image_resolution($val);
- }
-
- /**
- * @param $val
- */
- function set_z_index($val)
- {
- if (round($val) != $val && $val !== "auto") {
- return;
- }
-
- $this->_prop_cache["z_index"] = null;
- $this->_props["z_index"] = $val;
- }
-
- /**
- * @param $val
- */
- function set_counter_increment($val)
- {
- $val = trim($val);
- $value = null;
-
- if (in_array($val, array("none", "inherit"))) {
- $value = $val;
- } else {
- if (preg_match_all("/(" . self::CSS_IDENTIFIER . ")(?:\s+(" . self::CSS_INTEGER . "))?/", $val, $matches, PREG_SET_ORDER)) {
- $value = array();
- foreach ($matches as $match) {
- $value[$match[1]] = isset($match[2]) ? $match[2] : 1;
- }
- }
- }
-
- $this->_prop_cache["counter_increment"] = null;
- $this->_props["counter_increment"] = $value;
- }
-
- /**
- * @param FontMetrics $fontMetrics
- * @return $this
- */
- public function setFontMetrics(FontMetrics $fontMetrics)
- {
- $this->fontMetrics = $fontMetrics;
- return $this;
- }
-
- /**
- * @return FontMetrics
- */
- public function getFontMetrics()
- {
- return $this->fontMetrics;
- }
-
- /**
- * Generate a string representation of the Style
- *
- * This dumps the entire property array into a string via print_r. Useful
- * for debugging.
- *
- * @return string
- */
- /*DEBUGCSS print: see below additional debugging util*/
- function __toString()
- {
- return print_r(array_merge(array("parent_font_size" => $this->_parent_font_size),
- $this->_props), true);
- }
-
- /*DEBUGCSS*/
- function debug_print()
- {
- /*DEBUGCSS*/
- print "parent_font_size:" . $this->_parent_font_size . ";\n";
- /*DEBUGCSS*/
- foreach ($this->_props as $prop => $val) {
- /*DEBUGCSS*/
- print $prop . ':' . $val;
- /*DEBUGCSS*/
- if (isset($this->_important_props[$prop])) {
- /*DEBUGCSS*/
- print '!important';
- /*DEBUGCSS*/
- }
- /*DEBUGCSS*/
- print ";\n";
- /*DEBUGCSS*/
- }
- /*DEBUGCSS*/
- }
-}
diff --git a/library/vendor/dompdf/src/FontMetrics.php b/library/vendor/dompdf/src/FontMetrics.php
deleted file mode 100644
index 712bd0acc..000000000
--- a/library/vendor/dompdf/src/FontMetrics.php
+++ /dev/null
@@ -1,538 +0,0 @@
-
- * @author Helmut Tischer
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-namespace Dompdf;
-
-use FontLib\Font;
-
-/**
- * The font metrics class
- *
- * This class provides information about fonts and text. It can resolve
- * font names into actual installed font files, as well as determine the
- * size of text in a particular font and size.
- *
- * @static
- * @package dompdf
- */
-class FontMetrics
-{
- /**
- * Name of the font cache file
- *
- * This file must be writable by the webserver process only to update it
- * with save_font_families() after adding the .afm file references of a new font family
- * with FontMetrics::saveFontFamilies().
- * This is typically done only from command line with load_font.php on converting
- * ttf fonts to ufm with php-font-lib.
- */
- const CACHE_FILE = "dompdf_font_family_cache.php";
-
- /**
- * @var Canvas
- * @deprecated
- */
- protected $pdf;
-
- /**
- * Underlying {@link Canvas} object to perform text size calculations
- *
- * @var Canvas
- */
- protected $canvas;
-
- /**
- * Array of font family names to font files
- *
- * Usually cached by the {@link load_font.php} script
- *
- * @var array
- */
- protected $fontLookup = array();
-
- /**
- * @var Options
- */
- private $options;
-
- /**
- * Class initialization
- */
- public function __construct(Canvas $canvas, Options $options)
- {
- $this->setCanvas($canvas);
- $this->setOptions($options);
- $this->loadFontFamilies();
- }
-
- /**
- * @deprecated
- */
- public function save_font_families()
- {
- $this->saveFontFamilies();
- }
-
- /**
- * Saves the stored font family cache
- *
- * The name and location of the cache file are determined by {@link
- * FontMetrics::CACHE_FILE}. This file should be writable by the
- * webserver process.
- *
- * @see FontMetrics::loadFontFamilies()
- */
- public function saveFontFamilies()
- {
- // replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability)
- $cacheData = sprintf("fontLookup as $family => $variants) {
- $cacheData .= sprintf(" '%s' => array(%s", addslashes($family), PHP_EOL);
- foreach ($variants as $variant => $path) {
- $path = sprintf("'%s'", $path);
- $path = str_replace('\'' . $this->getOptions()->getFontDir() , '$fontDir . \'' , $path);
- $path = str_replace('\'' . $this->getOptions()->getRootDir() , '$rootDir . \'' , $path);
- $cacheData .= sprintf(" '%s' => %s,%s", $variant, $path, PHP_EOL);
- }
- $cacheData .= sprintf(" ),%s", PHP_EOL);
- }
- $cacheData .= ") ?>";
- file_put_contents($this->getCacheFile(), $cacheData);
- }
-
- /**
- * @deprecated
- */
- public function load_font_families()
- {
- $this->loadFontFamilies();
- }
-
- /**
- * Loads the stored font family cache
- *
- * @see FontMetrics::saveFontFamilies()
- */
- public function loadFontFamilies()
- {
- $fontDir = $this->getOptions()->getFontDir();
- $rootDir = $this->getOptions()->getRootDir();
-
- // FIXME: temporarily define constants for cache files <= v0.6.2
- if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
- if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
-
- $file = $rootDir . "/lib/fonts/dompdf_font_family_cache.dist.php";
- $distFonts = require $file;
-
- if (!is_readable($this->getCacheFile())) {
- $this->fontLookup = $distFonts;
- return;
- }
-
- $cacheData = require $this->getCacheFile();
-
- $this->fontLookup = array();
- if (is_array($this->fontLookup)) {
- foreach ($cacheData as $key => $value) {
- $this->fontLookup[stripslashes($key)] = $value;
- }
- }
-
- // Merge provided fonts
- $this->fontLookup += $distFonts;
- }
-
- /**
- * @param array $style
- * @param string $remote_file
- * @param resource $context
- * @return bool
- * @deprecated
- */
- public function register_font($style, $remote_file, $context = null)
- {
- return $this->registerFont($style, $remote_file);
- }
-
- /**
- * @param array $style
- * @param string $remoteFile
- * @param resource $context
- * @return bool
- */
- public function registerFont($style, $remoteFile, $context = null)
- {
- $fontname = mb_strtolower($style["family"]);
- $families = $this->getFontFamilies();
-
- $entry = array();
- if (isset($families[$fontname])) {
- $entry = $families[$fontname];
- }
-
- $styleString = $this->getType("{$style['weight']} {$style['style']}");
- if (isset($entry[$styleString])) {
- return true;
- }
-
- $fontDir = $this->getOptions()->getFontDir();
- $remoteHash = md5($remoteFile);
- $localFile = $fontDir . DIRECTORY_SEPARATOR . $remoteHash;
-
- $cacheEntry = $localFile;
- $localFile .= ".".strtolower(pathinfo(parse_url($remoteFile, PHP_URL_PATH),PATHINFO_EXTENSION));
-
- $entry[$styleString] = $cacheEntry;
-
- // Download the remote file
- list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);
- if (false === $remoteFileContent) {
- return false;
- }
-
- $localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-");
- file_put_contents($localTempFile, $remoteFileContent);
-
- $font = Font::load($localTempFile);
-
- if (!$font) {
- unlink($localTempFile);
- return false;
- }
-
- $font->parse();
- $font->saveAdobeFontMetrics("$cacheEntry.ufm");
- $font->close();
-
- unlink($localTempFile);
-
- if ( !file_exists("$cacheEntry.ufm") ) {
- return false;
- }
-
- // Save the changes
- file_put_contents($localFile, $remoteFileContent);
-
- if ( !file_exists($localFile) ) {
- unlink("$cacheEntry.ufm");
- return false;
- }
-
- $this->setFontFamily($fontname, $entry);
- $this->saveFontFamilies();
-
- return true;
- }
-
- /**
- * @param $text
- * @param $font
- * @param $size
- * @param float $word_spacing
- * @param float $char_spacing
- * @return float
- * @deprecated
- */
- public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
- {
- //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
- return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing);
- }
-
- /**
- * Calculates text size, in points
- *
- * @param string $text the text to be sized
- * @param string $font the desired font
- * @param float $size the desired font size
- * @param float $wordSpacing
- * @param float $charSpacing
- *
- * @internal param float $spacing word spacing, if any
- * @return float
- */
- public function getTextWidth($text, $font, $size, $wordSpacing = 0.0, $charSpacing = 0.0)
- {
- // @todo Make sure this cache is efficient before enabling it
- static $cache = array();
-
- if ($text === "") {
- return 0;
- }
-
- // Don't cache long strings
- $useCache = !isset($text[50]); // Faster than strlen
-
- $key = "$font/$size/$wordSpacing/$charSpacing";
-
- if ($useCache && isset($cache[$key][$text])) {
- return $cache[$key]["$text"];
- }
-
- $width = $this->getCanvas()->get_text_width($text, $font, $size, $wordSpacing, $charSpacing);
-
- if ($useCache) {
- $cache[$key][$text] = $width;
- }
-
- return $width;
- }
-
- /**
- * @param $font
- * @param $size
- * @return float
- * @deprecated
- */
- public function get_font_height($font, $size)
- {
- return $this->getFontHeight($font, $size);
- }
-
- /**
- * Calculates font height
- *
- * @param string $font
- * @param float $size
- *
- * @return float
- */
- public function getFontHeight($font, $size)
- {
- return $this->getCanvas()->get_font_height($font, $size);
- }
-
- /**
- * @param $family_raw
- * @param string $subtype_raw
- * @return string
- * @deprecated
- */
- public function get_font($family_raw, $subtype_raw = "normal")
- {
- return $this->getFont($family_raw, $subtype_raw);
- }
-
- /**
- * Resolves a font family & subtype into an actual font file
- * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
- * the particular font family has no suitable font file, the default font
- * ({@link Options::defaultFont}) is used. The font file returned
- * is the absolute pathname to the font file on the system.
- *
- * @param string $familyRaw
- * @param string $subtypeRaw
- *
- * @return string
- */
- public function getFont($familyRaw, $subtypeRaw = "normal")
- {
- static $cache = array();
-
- if (isset($cache[$familyRaw][$subtypeRaw])) {
- return $cache[$familyRaw][$subtypeRaw];
- }
-
- /* Allow calling for various fonts in search path. Therefore not immediately
- * return replacement on non match.
- * Only when called with NULL try replacement.
- * When this is also missing there is really trouble.
- * If only the subtype fails, nevertheless return failure.
- * Only on checking the fallback font, check various subtypes on same font.
- */
-
- $subtype = strtolower($subtypeRaw);
-
- if ($familyRaw) {
- $family = str_replace(array("'", '"'), "", strtolower($familyRaw));
-
- if (isset($this->fontLookup[$family][$subtype])) {
- return $cache[$familyRaw][$subtypeRaw] = $this->fontLookup[$family][$subtype];
- }
-
- return null;
- }
-
- $family = "serif";
-
- if (isset($this->fontLookup[$family][$subtype])) {
- return $cache[$familyRaw][$subtypeRaw] = $this->fontLookup[$family][$subtype];
- }
-
- if (!isset($this->fontLookup[$family])) {
- return null;
- }
-
- $family = $this->fontLookup[$family];
-
- foreach ($family as $sub => $font) {
- if (strpos($subtype, $sub) !== false) {
- return $cache[$familyRaw][$subtypeRaw] = $font;
- }
- }
-
- if ($subtype !== "normal") {
- foreach ($family as $sub => $font) {
- if ($sub !== "normal") {
- return $cache[$familyRaw][$subtypeRaw] = $font;
- }
- }
- }
-
- $subtype = "normal";
-
- if (isset($family[$subtype])) {
- return $cache[$familyRaw][$subtypeRaw] = $family[$subtype];
- }
-
- return null;
- }
-
- /**
- * @param $family
- * @return null|string
- * @deprecated
- */
- public function get_family($family)
- {
- return $this->getFamily($family);
- }
-
- /**
- * @param string $family
- * @return null|string
- */
- public function getFamily($family)
- {
- $family = str_replace(array("'", '"'), "", mb_strtolower($family));
-
- if (isset($this->fontLookup[$family])) {
- return $this->fontLookup[$family];
- }
-
- return null;
- }
-
- /**
- * @param $type
- * @return string
- * @deprecated
- */
- public function get_type($type)
- {
- return $this->getType($type);
- }
-
- /**
- * @param string $type
- * @return string
- */
- public function getType($type)
- {
- if (preg_match("/bold/i", $type)) {
- if (preg_match("/italic|oblique/i", $type)) {
- $type = "bold_italic";
- } else {
- $type = "bold";
- }
- } elseif (preg_match("/italic|oblique/i", $type)) {
- $type = "italic";
- } else {
- $type = "normal";
- }
-
- return $type;
- }
-
- /**
- * @return array
- * @deprecated
- */
- public function get_font_families()
- {
- return $this->getFontFamilies();
- }
-
- /**
- * Returns the current font lookup table
- *
- * @return array
- */
- public function getFontFamilies()
- {
- return $this->fontLookup;
- }
-
- /**
- * @param string $fontname
- * @param mixed $entry
- * @deprecated
- */
- public function set_font_family($fontname, $entry)
- {
- $this->setFontFamily($fontname, $entry);
- }
-
- /**
- * @param string $fontname
- * @param mixed $entry
- */
- public function setFontFamily($fontname, $entry)
- {
- $this->fontLookup[mb_strtolower($fontname)] = $entry;
- }
-
- /**
- * @return string
- */
- public function getCacheFile()
- {
- return $this->getOptions()->getFontDir() . DIRECTORY_SEPARATOR . self::CACHE_FILE;
- }
-
- /**
- * @param Options $options
- * @return $this
- */
- public function setOptions(Options $options)
- {
- $this->options = $options;
- return $this;
- }
-
- /**
- * @return Options
- */
- public function getOptions()
- {
- return $this->options;
- }
-
- /**
- * @param Canvas $canvas
- * @return $this
- */
- public function setCanvas(Canvas $canvas)
- {
- $this->canvas = $canvas;
- // Still write deprecated pdf for now. It might be used by a parent class.
- $this->pdf = $canvas;
- return $this;
- }
-
- /**
- * @return Canvas
- */
- public function getCanvas()
- {
- return $this->canvas;
- }
-}
\ No newline at end of file
diff --git a/library/vendor/dompdf/src/Frame/FrameList.php b/library/vendor/dompdf/src/Frame/FrameList.php
deleted file mode 100644
index 37d9990a1..000000000
--- a/library/vendor/dompdf/src/Frame/FrameList.php
+++ /dev/null
@@ -1,35 +0,0 @@
-_frame = $frame;
- }
-
- /**
- * @return FrameListIterator
- */
- function getIterator()
- {
- return new FrameListIterator($this->_frame);
- }
-}
diff --git a/library/vendor/dompdf/src/Frame/FrameListIterator.php b/library/vendor/dompdf/src/Frame/FrameListIterator.php
deleted file mode 100644
index ada9dde18..000000000
--- a/library/vendor/dompdf/src/Frame/FrameListIterator.php
+++ /dev/null
@@ -1,91 +0,0 @@
-_parent = $frame;
- $this->_cur = $frame->get_first_child();
- $this->_num = 0;
- }
-
- /**
- *
- */
- public function rewind()
- {
- $this->_cur = $this->_parent->get_first_child();
- $this->_num = 0;
- }
-
- /**
- * @return bool
- */
- public function valid()
- {
- return isset($this->_cur); // && ($this->_cur->get_prev_sibling() === $this->_prev);
- }
-
- /**
- * @return int
- */
- public function key()
- {
- return $this->_num;
- }
-
- /**
- * @return Frame
- */
- public function current()
- {
- return $this->_cur;
- }
-
- /**
- * @return Frame
- */
- public function next()
- {
- $ret = $this->_cur;
- if (!$ret) {
- return null;
- }
-
- $this->_cur = $this->_cur->get_next_sibling();
- $this->_num++;
- return $ret;
- }
-}
\ No newline at end of file
diff --git a/library/vendor/dompdf/src/Frame/FrameTreeList.php b/library/vendor/dompdf/src/Frame/FrameTreeList.php
deleted file mode 100644
index f8b996c68..000000000
--- a/library/vendor/dompdf/src/Frame/FrameTreeList.php
+++ /dev/null
@@ -1,35 +0,0 @@
-_root = $root;
- }
-
- /**
- * @return FrameTreeIterator
- */
- public function getIterator()
- {
- return new FrameTreeIterator($this->_root);
- }
-}
diff --git a/library/vendor/dompdf/src/FrameDecorator/Block.php b/library/vendor/dompdf/src/FrameDecorator/Block.php
deleted file mode 100644
index b65b3e104..000000000
--- a/library/vendor/dompdf/src/FrameDecorator/Block.php
+++ /dev/null
@@ -1,284 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameDecorator;
-
-use Dompdf\Dompdf;
-use Dompdf\Frame;
-use Dompdf\LineBox;
-
-/**
- * Decorates frames for block layout
- *
- * @access private
- * @package dompdf
- */
-class Block extends AbstractFrameDecorator
-{
- /**
- * Current line index
- *
- * @var int
- */
- protected $_cl;
-
- /**
- * The block's line boxes
- *
- * @var LineBox[]
- */
- protected $_line_boxes;
-
- /**
- * Block constructor.
- * @param Frame $frame
- * @param Dompdf $dompdf
- */
- function __construct(Frame $frame, Dompdf $dompdf)
- {
- parent::__construct($frame, $dompdf);
-
- $this->_line_boxes = array(new LineBox($this));
- $this->_cl = 0;
- }
-
- /**
- *
- */
- function reset()
- {
- parent::reset();
-
- $this->_line_boxes = array(new LineBox($this));
- $this->_cl = 0;
- }
-
- /**
- * @return LineBox
- */
- function get_current_line_box()
- {
- return $this->_line_boxes[$this->_cl];
- }
-
- /**
- * @return integer
- */
- function get_current_line_number()
- {
- return $this->_cl;
- }
-
- /**
- * @return LineBox[]
- */
- function get_line_boxes()
- {
- return $this->_line_boxes;
- }
-
- /**
- * @param integer $line_number
- * @return integer
- */
- function set_current_line_number($line_number)
- {
- $line_boxes_count = count($this->_line_boxes);
- $cl = max(min($line_number, $line_boxes_count), 0);
- return ($this->_cl = $cl);
- }
-
- /**
- * @param integer $i
- */
- function clear_line($i)
- {
- if (isset($this->_line_boxes[$i])) {
- unset($this->_line_boxes[$i]);
- }
- }
-
- /**
- * @param Frame $frame
- */
- function add_frame_to_line(Frame $frame)
- {
- if (!$frame->is_in_flow()) {
- return;
- }
-
- $style = $frame->get_style();
-
- $frame->set_containing_line($this->_line_boxes[$this->_cl]);
-
- /*
- // Adds a new line after a block, only if certain conditions are met
- if ((($frame instanceof Inline && $frame->get_node()->nodeName !== "br") ||
- $frame instanceof Text && trim($frame->get_text())) &&
- ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" &&
- $this->_line_boxes[$this->_cl]->w > 0 )) {
-
- $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
- $this->add_line();
-
- // Add each child of the inline frame to the line individually
- foreach ($frame->get_children() as $child)
- $this->add_frame_to_line( $child );
- }
- else*/
-
- // Handle inline frames (which are effectively wrappers)
- if ($frame instanceof Inline) {
- // Handle line breaks
- if ($frame->get_node()->nodeName === "br") {
- $this->maximize_line_height($style->length_in_pt($style->line_height), $frame);
- $this->add_line(true);
- }
-
- return;
- }
-
- // Trim leading text if this is an empty line. Kinda a hack to put it here,
- // but what can you do...
- if ($this->get_current_line_box()->w == 0 &&
- $frame->is_text_node() &&
- !$frame->is_pre()
- ) {
- $frame->set_text(ltrim($frame->get_text()));
- $frame->recalculate_width();
- }
-
- $w = $frame->get_margin_width();
-
- // FIXME: Why? Doesn't quite seem to be the correct thing to do,
- // but does appear to be necessary. Hack to handle wrapped white space?
- if ($w == 0 && $frame->get_node()->nodeName !== "hr") {
- return;
- }
-
- // Debugging code:
- /*
- Helpers::pre_r("\nAdding frame to line: ");
-
- // Helpers::pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");
- // Helpers::pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
- if ( $frame->is_text_node() )
- Helpers::pre_r('"'.$frame->get_node()->nodeValue.'"');
-
- Helpers::pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w);
- Helpers::pre_r("Frame: " . get_class($frame));
- Helpers::pre_r("Frame width: " . $w);
- Helpers::pre_r("Frame height: " . $frame->get_margin_height());
- Helpers::pre_r("Containing block width: " . $this->get_containing_block("w"));
- */
- // End debugging
-
- $line = $this->_line_boxes[$this->_cl];
- if ($line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) {
- $this->add_line();
- }
-
- $frame->position();
-
- $current_line = $this->_line_boxes[$this->_cl];
- $current_line->add_frame($frame);
-
- if ($frame->is_text_node()) {
- $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text())));
- }
-
- $this->increase_line_width($w);
-
- $this->maximize_line_height($frame->get_margin_height(), $frame);
- }
-
- /**
- * @param Frame $frame
- */
- function remove_frames_from_line(Frame $frame)
- {
- // Search backwards through the lines for $frame
- $i = $this->_cl;
- $j = null;
-
- while ($i >= 0) {
- if (($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false) {
- break;
- }
-
- $i--;
- }
-
- if ($j === false) {
- return;
- }
-
- // Remove $frame and all frames that follow
- while ($j < count($this->_line_boxes[$i]->get_frames())) {
- $frames = $this->_line_boxes[$i]->get_frames();
- $f = $frames[$j];
- $frames[$j] = null;
- unset($frames[$j]);
- $j++;
- $this->_line_boxes[$i]->w -= $f->get_margin_width();
- }
-
- // Recalculate the height of the line
- $h = 0;
- foreach ($this->_line_boxes[$i]->get_frames() as $f) {
- $h = max($h, $f->get_margin_height());
- }
-
- $this->_line_boxes[$i]->h = $h;
-
- // Remove all lines that follow
- while ($this->_cl > $i) {
- $this->_line_boxes[$this->_cl] = null;
- unset($this->_line_boxes[$this->_cl]);
- $this->_cl--;
- }
- }
-
- /**
- * @param float $w
- */
- function increase_line_width($w)
- {
- $this->_line_boxes[$this->_cl]->w += $w;
- }
-
- /**
- * @param $val
- * @param Frame $frame
- */
- function maximize_line_height($val, Frame $frame)
- {
- if ($val > $this->_line_boxes[$this->_cl]->h) {
- $this->_line_boxes[$this->_cl]->tallest_frame = $frame;
- $this->_line_boxes[$this->_cl]->h = $val;
- }
- }
-
- /**
- * @param bool $br
- */
- function add_line($br = false)
- {
-
-// if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 ||
-// return;
-
- $this->_line_boxes[$this->_cl]->br = $br;
- $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h;
-
- $new_line = new LineBox($this, $y);
-
- $this->_line_boxes[++$this->_cl] = $new_line;
- }
-
- //........................................................................
-}
diff --git a/library/vendor/dompdf/src/FrameDecorator/Inline.php b/library/vendor/dompdf/src/FrameDecorator/Inline.php
deleted file mode 100644
index e831fd85e..000000000
--- a/library/vendor/dompdf/src/FrameDecorator/Inline.php
+++ /dev/null
@@ -1,106 +0,0 @@
-
- * @author Helmut Tischer
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameDecorator;
-
-use DOMElement;
-use Dompdf\Dompdf;
-use Dompdf\Frame;
-use Dompdf\Exception;
-
-/**
- * Decorates frames for inline layout
- *
- * @access private
- * @package dompdf
- */
-class Inline extends AbstractFrameDecorator
-{
-
- /**
- * Inline constructor.
- * @param Frame $frame
- * @param Dompdf $dompdf
- */
- function __construct(Frame $frame, Dompdf $dompdf)
- {
- parent::__construct($frame, $dompdf);
- }
-
- /**
- * @param Frame|null $frame
- * @param bool $force_pagebreak
- * @throws Exception
- */
- function split(Frame $frame = null, $force_pagebreak = false)
- {
- if (is_null($frame)) {
- $this->get_parent()->split($this, $force_pagebreak);
- return;
- }
-
- if ($frame->get_parent() !== $this) {
- throw new Exception("Unable to split: frame is not a child of this one.");
- }
-
- $node = $this->_frame->get_node();
-
- if ($node instanceof DOMElement && $node->hasAttribute("id")) {
- $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
- $node->removeAttribute("id");
- }
-
- $split = $this->copy($node->cloneNode());
- // if this is a generated node don't propagate the content style
- if ($split->get_node()->nodeName == "dompdf_generated") {
- $split->get_style()->content = "normal";
- }
- $this->get_parent()->insert_child_after($split, $this);
-
- // Unset the current node's right style properties
- $style = $this->_frame->get_style();
- $style->margin_right = 0;
- $style->padding_right = 0;
- $style->border_right_width = 0;
-
- // Unset the split node's left style properties since we don't want them
- // to propagate
- $style = $split->get_style();
- $style->margin_left = 0;
- $style->padding_left = 0;
- $style->border_left_width = 0;
-
- //On continuation of inline element on next line,
- //don't repeat non-vertically repeatble background images
- //See e.g. in testcase image_variants, long desriptions
- if (($url = $style->background_image) && $url !== "none"
- && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y"
- ) {
- $style->background_image = "none";
- }
-
- // Add $frame and all following siblings to the new split node
- $iter = $frame;
- while ($iter) {
- $frame = $iter;
- $iter = $iter->get_next_sibling();
- $frame->reset();
- $split->append_child($frame);
- }
-
- $page_breaks = array("always", "left", "right");
- $frame_style = $frame->get_style();
- if ($force_pagebreak ||
- in_array($frame_style->page_break_before, $page_breaks) ||
- in_array($frame_style->page_break_after, $page_breaks)
- ) {
- $this->get_parent()->split($split, true);
- }
- }
-
-}
diff --git a/library/vendor/dompdf/src/FrameDecorator/ListBullet.php b/library/vendor/dompdf/src/FrameDecorator/ListBullet.php
deleted file mode 100644
index 5b2311886..000000000
--- a/library/vendor/dompdf/src/FrameDecorator/ListBullet.php
+++ /dev/null
@@ -1,87 +0,0 @@
-
- * @author Helmut Tischer
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameDecorator;
-
-use Dompdf\Dompdf;
-use Dompdf\Frame;
-
-/**
- * Decorates frames for list bullet rendering
- *
- * @package dompdf
- */
-class ListBullet extends AbstractFrameDecorator
-{
-
- const BULLET_PADDING = 1; // Distance from bullet to text in pt
- // As fraction of font size (including descent). See also DECO_THICKNESS.
- const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04
- const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now.
- const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent.
-
- static $BULLET_TYPES = array("disc", "circle", "square");
-
- /**
- * ListBullet constructor.
- * @param Frame $frame
- * @param Dompdf $dompdf
- */
- function __construct(Frame $frame, Dompdf $dompdf)
- {
- parent::__construct($frame, $dompdf);
- }
-
- /**
- * @return float|int
- */
- function get_margin_width()
- {
- $style = $this->_frame->get_style();
-
- if ($style->list_style_type === "none") {
- return 0;
- }
-
- return $style->font_size * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
- }
-
- /**
- * hits only on "inset" lists items, to increase height of box
- *
- * @return float|int
- */
- function get_margin_height()
- {
- $style = $this->_frame->get_style();
-
- if ($style->list_style_type === "none") {
- return 0;
- }
-
- return $style->font_size * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
- }
-
- /**
- * @return float|int
- */
- function get_width()
- {
- return $this->get_margin_width();
- }
-
- /**
- * @return float|int
- */
- function get_height()
- {
- return $this->get_margin_height();
- }
-
- //........................................................................
-}
diff --git a/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php b/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php
deleted file mode 100644
index 5bd93a5de..000000000
--- a/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php
+++ /dev/null
@@ -1,154 +0,0 @@
-
- * @author Helmut Tischer
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameDecorator;
-
-use Dompdf\Dompdf;
-use Dompdf\Frame;
-use Dompdf\Helpers;
-
-/**
- * Decorates frames for list bullets with custom images
- *
- * @package dompdf
- */
-class ListBulletImage extends AbstractFrameDecorator
-{
-
- /**
- * The underlying image frame
- *
- * @var Image
- */
- protected $_img;
-
- /**
- * The image's width in pixels
- *
- * @var int
- */
- protected $_width;
-
- /**
- * The image's height in pixels
- *
- * @var int
- */
- protected $_height;
-
- /**
- * Class constructor
- *
- * @param Frame $frame the bullet frame to decorate
- * @param Dompdf $dompdf the document's dompdf object
- */
- function __construct(Frame $frame, Dompdf $dompdf)
- {
- $style = $frame->get_style();
- $url = $style->list_style_image;
- $frame->get_node()->setAttribute("src", $url);
- $this->_img = new Image($frame, $dompdf);
- parent::__construct($this->_img, $dompdf);
- list($width, $height) = Helpers::dompdf_getimagesize($this->_img->get_image_url(), $dompdf->getHttpContext());
-
- // Resample the bullet image to be consistent with 'auto' sized images
- // See also Image::get_min_max_width
- // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
- $dpi = $this->_dompdf->getOptions()->getDpi();
- $this->_width = ((float)rtrim($width, "px") * 72) / $dpi;
- $this->_height = ((float)rtrim($height, "px") * 72) / $dpi;
-
- //If an image is taller as the containing block/box, the box should be extended.
- //Neighbour elements are overwriting the overlapping image areas.
- //Todo: Where can the box size be extended?
- //Code below has no effect.
- //See block_frame_reflower _calculate_restricted_height
- //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S.
- //Leave for now
- //if ($style->min_height < $this->_height ) {
- // $style->min_height = $this->_height;
- //}
- //$style->height = "auto";
- }
-
- /**
- * Return the bullet's width
- *
- * @return int
- */
- function get_width()
- {
- //ignore image width, use same width as on predefined bullet ListBullet
- //for proper alignment of bullet image and text. Allow image to not fitting on left border.
- //This controls the distance between bullet image and text
- //return $this->_width;
- return $this->_frame->get_style()->get_font_size() * ListBullet::BULLET_SIZE +
- 2 * ListBullet::BULLET_PADDING;
- }
-
- /**
- * Return the bullet's height
- *
- * @return int
- */
- function get_height()
- {
- //based on image height
- return $this->_height;
- }
-
- /**
- * Override get_margin_width
- *
- * @return int
- */
- function get_margin_width()
- {
- //ignore image width, use same width as on predefined bullet ListBullet
- //for proper alignment of bullet image and text. Allow image to not fitting on left border.
- //This controls the extra indentation of text to make room for the bullet image.
- //Here use actual image size, not predefined bullet size
- //return $this->_frame->get_style()->get_font_size()*ListBullet::BULLET_SIZE +
- // 2 * ListBullet::BULLET_PADDING;
-
- // Small hack to prevent indenting of list text
- // Image Might not exist, then position like on list_bullet_frame_decorator fallback to none.
- if ($this->_frame->get_style()->list_style_position === "outside" || $this->_width == 0) {
- return 0;
- }
- //This aligns the "inside" image position with the text.
- //The text starts to the right of the image.
- //Between the image and the text there is an added margin of image width.
- //Where this comes from is unknown.
- //The corresponding ListBullet sets a smaller margin. bullet size?
- return $this->_width + 2 * ListBullet::BULLET_PADDING;
- }
-
- /**
- * Override get_margin_height()
- *
- * @return int
- */
- function get_margin_height()
- {
- //Hits only on "inset" lists items, to increase height of box
- //based on image height
- return $this->_height + 2 * ListBullet::BULLET_PADDING;
- }
-
- /**
- * Return image url
- *
- * @return string
- */
- function get_image_url()
- {
- return $this->_img->get_image_url();
- }
-
-}
diff --git a/library/vendor/dompdf/src/FrameDecorator/Table.php b/library/vendor/dompdf/src/FrameDecorator/Table.php
deleted file mode 100644
index 3776c60be..000000000
--- a/library/vendor/dompdf/src/FrameDecorator/Table.php
+++ /dev/null
@@ -1,398 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameDecorator;
-
-use Dompdf\Cellmap;
-use DOMNode;
-use Dompdf\Dompdf;
-use Dompdf\Frame;
-use Dompdf\Frame\Factory;
-
-/**
- * Decorates Frames for table layout
- *
- * @package dompdf
- */
-class Table extends AbstractFrameDecorator
-{
- public static $VALID_CHILDREN = array(
- "table-row-group",
- "table-row",
- "table-header-group",
- "table-footer-group",
- "table-column",
- "table-column-group",
- "table-caption",
- "table-cell"
- );
-
- public static $ROW_GROUPS = array(
- 'table-row-group',
- 'table-header-group',
- 'table-footer-group'
- );
-
- /**
- * The Cellmap object for this table. The cellmap maps table cells
- * to rows and columns, and aids in calculating column widths.
- *
- * @var \Dompdf\Cellmap
- */
- protected $_cellmap;
-
- /**
- * The minimum width of the table, in pt
- *
- * @var float
- */
- protected $_min_width;
-
- /**
- * The maximum width of the table, in pt
- *
- * @var float
- */
- protected $_max_width;
-
- /**
- * Table header rows. Each table header is duplicated when a table
- * spans pages.
- *
- * @var array
- */
- protected $_headers;
-
- /**
- * Table footer rows. Each table footer is duplicated when a table
- * spans pages.
- *
- * @var array
- */
- protected $_footers;
-
- /**
- * Class constructor
- *
- * @param Frame $frame the frame to decorate
- * @param Dompdf $dompdf
- */
- public function __construct(Frame $frame, Dompdf $dompdf)
- {
- parent::__construct($frame, $dompdf);
- $this->_cellmap = new Cellmap($this);
-
- if ($frame->get_style()->table_layout === "fixed") {
- $this->_cellmap->set_layout_fixed(true);
- }
-
- $this->_min_width = null;
- $this->_max_width = null;
- $this->_headers = array();
- $this->_footers = array();
- }
-
- public function reset()
- {
- parent::reset();
- $this->_cellmap->reset();
- $this->_min_width = null;
- $this->_max_width = null;
- $this->_headers = array();
- $this->_footers = array();
- $this->_reflower->reset();
- }
-
- //........................................................................
-
- /**
- * split the table at $row. $row and all subsequent rows will be
- * added to the clone. This method is overidden in order to remove
- * frames from the cellmap properly.
- *
- * @param Frame $child
- * @param bool $force_pagebreak
- *
- * @return void
- */
- public function split(Frame $child = null, $force_pagebreak = false)
- {
- if (is_null($child)) {
- parent::split();
-
- return;
- }
-
- // If $child is a header or if it is the first non-header row, do
- // not duplicate headers, simply move the table to the next page.
- if (count($this->_headers) && !in_array($child, $this->_headers, true) &&
- !in_array($child->get_prev_sibling(), $this->_headers, true)
- ) {
- $first_header = null;
-
- // Insert copies of the table headers before $child
- foreach ($this->_headers as $header) {
-
- $new_header = $header->deep_copy();
-
- if (is_null($first_header)) {
- $first_header = $new_header;
- }
-
- $this->insert_child_before($new_header, $child);
- }
-
- parent::split($first_header);
-
- } elseif (in_array($child->get_style()->display, self::$ROW_GROUPS)) {
-
- // Individual rows should have already been handled
- parent::split($child);
-
- } else {
-
- $iter = $child;
-
- while ($iter) {
- $this->_cellmap->remove_row($iter);
- $iter = $iter->get_next_sibling();
- }
-
- parent::split($child);
- }
- }
-
- /**
- * Return a copy of this frame with $node as its node
- *
- * @param DOMNode $node
- *
- * @return Frame
- */
- public function copy(DOMNode $node)
- {
- $deco = parent::copy($node);
-
- // In order to keep columns' widths through pages
- $deco->_cellmap->set_columns($this->_cellmap->get_columns());
- $deco->_cellmap->lock_columns();
-
- return $deco;
- }
-
- /**
- * Static function to locate the parent table of a frame
- *
- * @param Frame $frame
- *
- * @return Table the table that is an ancestor of $frame
- */
- public static function find_parent_table(Frame $frame)
- {
- while ($frame = $frame->get_parent()) {
- if ($frame->is_table()) {
- break;
- }
- }
-
- return $frame;
- }
-
- /**
- * Return this table's Cellmap
- *
- * @return \Dompdf\Cellmap
- */
- public function get_cellmap()
- {
- return $this->_cellmap;
- }
-
- /**
- * Return the minimum width of this table
- *
- * @return float
- */
- public function get_min_width()
- {
- return $this->_min_width;
- }
-
- /**
- * Return the maximum width of this table
- *
- * @return float
- */
- public function get_max_width()
- {
- return $this->_max_width;
- }
-
- /**
- * Set the minimum width of the table
- *
- * @param float $width the new minimum width
- */
- public function set_min_width($width)
- {
- $this->_min_width = $width;
- }
-
- /**
- * Set the maximum width of the table
- *
- * @param float $width the new maximum width
- */
- public function set_max_width($width)
- {
- $this->_max_width = $width;
- }
-
- /**
- * Restructure tree so that the table has the correct structure.
- * Invalid children (i.e. all non-table-rows) are moved below the
- * table.
- *
- * @fixme #1363 Method has some bugs. $table_row has not been initialized and lookup most likely could return an
- * array of Style instead a Style Object
- */
- public function normalise()
- {
- // Store frames generated by invalid tags and move them outside the table
- $erroneous_frames = array();
- $anon_row = false;
- $iter = $this->get_first_child();
- while ($iter) {
- $child = $iter;
- $iter = $iter->get_next_sibling();
-
- $display = $child->get_style()->display;
-
- if ($anon_row) {
-
- if ($display === "table-row") {
- // Add the previous anonymous row
- $this->insert_child_before($table_row, $child);
-
- $table_row->normalise();
- $child->normalise();
- $this->_cellmap->add_row();
- $anon_row = false;
- continue;
- }
-
- // add the child to the anonymous row
- $table_row->append_child($child);
- continue;
-
- } else {
-
- if ($display === "table-row") {
- $child->normalise();
- continue;
- }
-
- if ($display === "table-cell") {
- $css = $this->get_style()->get_stylesheet();
-
- // Create an anonymous table row group
- $tbody = $this->get_node()->ownerDocument->createElement("tbody");
-
- $frame = new Frame($tbody);
-
- $style = $css->create_style();
- $style->inherit($this->get_style());
-
- // Lookup styles for tbody tags. If the user wants styles to work
- // better, they should make the tbody explicit... I'm not going to
- // try to guess what they intended.
- if ($tbody_style = $css->lookup("tbody")) {
- $style->merge($tbody_style);
- }
- $style->display = 'table-row-group';
-
- // Okay, I have absolutely no idea why I need this clone here, but
- // if it's omitted, php (as of 2004-07-28) segfaults.
- $frame->set_style($style);
- $table_row_group = Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
-
- // Create an anonymous table row
- $tr = $this->get_node()->ownerDocument->createElement("tr");
-
- $frame = new Frame($tr);
-
- $style = $css->create_style();
- $style->inherit($this->get_style());
-
- // Lookup styles for tr tags. If the user wants styles to work
- // better, they should make the tr explicit... I'm not going to
- // try to guess what they intended.
- if ($tr_style = $css->lookup("tr")) {
- $style->merge($tr_style);
- }
- $style->display = 'table-row';
-
- // Okay, I have absolutely no idea why I need this clone here, but
- // if it's omitted, php (as of 2004-07-28) segfaults.
- $frame->set_style(clone $style);
- $table_row = Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
-
- // Add the cell to the row
- $table_row->append_child($child, true);
-
- // Add the tr to the tbody
- $table_row_group->append_child($table_row, true);
-
- $anon_row = true;
- continue;
- }
-
- if (!in_array($display, self::$VALID_CHILDREN)) {
- $erroneous_frames[] = $child;
- continue;
- }
-
- // Normalise other table parts (i.e. row groups)
- foreach ($child->get_children() as $grandchild) {
- if ($grandchild->get_style()->display === "table-row") {
- $grandchild->normalise();
- }
- }
-
- // Add headers and footers
- if ($display === "table-header-group") {
- $this->_headers[] = $child;
- } elseif ($display === "table-footer-group") {
- $this->_footers[] = $child;
- }
- }
- }
-
- if ($anon_row && $table_row_group instanceof AbstractFrameDecorator) {
- // Add the row to the table
- $this->_frame->append_child($table_row_group->_frame);
- $table_row->normalise();
- }
-
- foreach ($erroneous_frames as $frame) {
- $this->move_after($frame);
- }
- }
-
- //........................................................................
-
- /**
- * Moves the specified frame and it's corresponding node outside of
- * the table.
- *
- * @param Frame $frame the frame to move
- */
- public function move_after(Frame $frame)
- {
- $this->get_parent()->insert_child_after($frame, $this);
- }
-}
\ No newline at end of file
diff --git a/library/vendor/dompdf/src/FrameDecorator/TableRow.php b/library/vendor/dompdf/src/FrameDecorator/TableRow.php
deleted file mode 100644
index 4c6dcb5f9..000000000
--- a/library/vendor/dompdf/src/FrameDecorator/TableRow.php
+++ /dev/null
@@ -1,68 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameDecorator;
-
-use Dompdf\Dompdf;
-use Dompdf\Frame;
-use Dompdf\FrameDecorator\Table as TableFrameDecorator;
-
-/**
- * Decorates Frames for table row layout
- *
- * @package dompdf
- */
-class TableRow extends AbstractFrameDecorator
-{
- /**
- * TableRow constructor.
- * @param Frame $frame
- * @param Dompdf $dompdf
- */
- function __construct(Frame $frame, Dompdf $dompdf)
- {
- parent::__construct($frame, $dompdf);
- }
-
- //........................................................................
-
- /**
- * Remove all non table-cell frames from this row and move them after
- * the table.
- */
- function normalise()
- {
- // Find our table parent
- $p = TableFrameDecorator::find_parent_table($this);
-
- $erroneous_frames = array();
- foreach ($this->get_children() as $child) {
- $display = $child->get_style()->display;
-
- if ($display !== "table-cell") {
- $erroneous_frames[] = $child;
- }
- }
-
- // dump the extra nodes after the table.
- foreach ($erroneous_frames as $frame) {
- $p->move_after($frame);
- }
- }
-
- function split(Frame $child = null, $force_pagebreak = false)
- {
- $this->_already_pushed = true;
-
- if (is_null($child)) {
- parent::split();
- return;
- }
-
- parent::split($child, $force_pagebreak);
- }
-}
diff --git a/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php b/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php
deleted file mode 100644
index 946d0969e..000000000
--- a/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php
+++ /dev/null
@@ -1,529 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameReflower;
-
-use Dompdf\Adapter\CPDF;
-use Dompdf\Css\Style;
-use Dompdf\Dompdf;
-use Dompdf\Helpers;
-use Dompdf\Frame;
-use Dompdf\FrameDecorator\Block;
-use Dompdf\Frame\Factory;
-
-/**
- * Base reflower class
- *
- * Reflower objects are responsible for determining the width and height of
- * individual frames. They also create line and page breaks as necessary.
- *
- * @package dompdf
- */
-abstract class AbstractFrameReflower
-{
-
- /**
- * Frame for this reflower
- *
- * @var Frame
- */
- protected $_frame;
-
- /**
- * Cached min/max size
- *
- * @var array
- */
- protected $_min_max_cache;
-
- /**
- * AbstractFrameReflower constructor.
- * @param Frame $frame
- */
- function __construct(Frame $frame)
- {
- $this->_frame = $frame;
- $this->_min_max_cache = null;
- }
-
- function dispose()
- {
- }
-
- /**
- * @return Dompdf
- */
- function get_dompdf()
- {
- return $this->_frame->get_dompdf();
- }
-
- /**
- * Collapse frames margins
- * http://www.w3.org/TR/CSS2/box.html#collapsing-margins
- */
- protected function _collapse_margins()
- {
- $frame = $this->_frame;
- $cb = $frame->get_containing_block();
- $style = $frame->get_style();
-
- // Margins of float/absolutely positioned/inline-block elements do not collapse.
- if (!$frame->is_in_flow() || $frame->is_inline_block()) {
- return;
- }
-
- $t = $style->length_in_pt($style->margin_top, $cb["h"]);
- $b = $style->length_in_pt($style->margin_bottom, $cb["h"]);
-
- // Handle 'auto' values
- if ($t === "auto") {
- $style->margin_top = "0pt";
- $t = 0;
- }
-
- if ($b === "auto") {
- $style->margin_bottom = "0pt";
- $b = 0;
- }
-
- // Collapse vertical margins:
- $n = $frame->get_next_sibling();
- if ( $n && !$n->is_block() & !$n->is_table() ) {
- while ($n = $n->get_next_sibling()) {
- if ($n->is_block() || $n->is_table()) {
- break;
- }
-
- if (!$n->get_first_child()) {
- $n = null;
- break;
- }
- }
- }
-
- if ($n) {
- $n_style = $n->get_style();
- $n_t = (float)$n_style->length_in_pt($n_style->margin_top, $cb["h"]);
-
- $b = $this->_get_collapsed_margin_length($b, $n_t);
- $style->margin_bottom = $b . "pt";
- $n_style->margin_top = "0pt";
- }
-
- // Collapse our first child's margin, if there is no border or padding
- if ($style->border_top_width == 0 && $style->length_in_pt($style->padding_top) == 0) {
- $f = $this->_frame->get_first_child();
- if ( $f && !$f->is_block() && !$f->is_table() ) {
- while ( $f = $f->get_next_sibling() ) {
- if ( $f->is_block() || $f->is_table() ) {
- break;
- }
-
- if ( !$f->get_first_child() ) {
- $f = null;
- break;
- }
- }
- }
-
- // Margin are collapsed only between block-level boxes
- if ($f) {
- $f_style = $f->get_style();
- $f_t = (float)$f_style->length_in_pt($f_style->margin_top, $cb["h"]);
-
- $t = $this->_get_collapsed_margin_length($t, $f_t);
- $style->margin_top = $t."pt";
- $f_style->margin_top = "0pt";
- }
- }
-
- // Collapse our last child's margin, if there is no border or padding
- if ($style->border_bottom_width == 0 && $style->length_in_pt($style->padding_bottom) == 0) {
- $l = $this->_frame->get_last_child();
- if ( $l && !$l->is_block() && !$l->is_table() ) {
- while ( $l = $l->get_prev_sibling() ) {
- if ( $l->is_block() || $l->is_table() ) {
- break;
- }
-
- if ( !$l->get_last_child() ) {
- $l = null;
- break;
- }
- }
- }
-
- // Margin are collapsed only between block-level boxes
- if ($l) {
- $l_style = $l->get_style();
- $l_b = (float)$l_style->length_in_pt($l_style->margin_bottom, $cb["h"]);
-
- $b = $this->_get_collapsed_margin_length($b, $l_b);
- $style->margin_bottom = $b."pt";
- $l_style->margin_bottom = "0pt";
- }
- }
- }
-
- /**
- * Get the combined (collapsed) length of two adjoining margins.
- *
- * See http://www.w3.org/TR/CSS2/box.html#collapsing-margins.
- *
- * @param number $length1
- * @param number $length2
- * @return number
- */
- private function _get_collapsed_margin_length($length1, $length2)
- {
- if ($length1 < 0 && $length2 < 0) {
- return min($length1, $length2); // min(x, y) = - max(abs(x), abs(y)), if x < 0 && y < 0
- }
-
- if ($length1 < 0 || $length2 < 0) {
- return $length1 + $length2; // x + y = x - abs(y), if y < 0
- }
-
- return max($length1, $length2);
- }
-
- /**
- * @param Block|null $block
- * @return mixed
- */
- abstract function reflow(Block $block = null);
-
- /**
- * Required for table layout: Returns an array(0 => min, 1 => max, "min"
- * => min, "max" => max) of the minimum and maximum widths of this frame.
- * This provides a basic implementation. Child classes should override
- * this if necessary.
- *
- * @return array|null
- */
- function get_min_max_width()
- {
- if (!is_null($this->_min_max_cache)) {
- return $this->_min_max_cache;
- }
-
- $style = $this->_frame->get_style();
-
- // Account for margins & padding
- $dims = array($style->padding_left,
- $style->padding_right,
- $style->border_left_width,
- $style->border_right_width,
- $style->margin_left,
- $style->margin_right);
-
- $cb_w = $this->_frame->get_containing_block("w");
- $delta = (float)$style->length_in_pt($dims, $cb_w);
-
- // Handle degenerate case
- if (!$this->_frame->get_first_child()) {
- return $this->_min_max_cache = array(
- $delta, $delta,
- "min" => $delta,
- "max" => $delta,
- );
- }
-
- $low = array();
- $high = array();
-
- for ($iter = $this->_frame->get_children()->getIterator(); $iter->valid(); $iter->next()) {
- $inline_min = 0;
- $inline_max = 0;
-
- // Add all adjacent inline widths together to calculate max width
- while ($iter->valid() && in_array($iter->current()->get_style()->display, Style::$INLINE_TYPES)) {
- $child = $iter->current();
-
- $minmax = $child->get_min_max_width();
-
- if (in_array($iter->current()->get_style()->white_space, array("pre", "nowrap"))) {
- $inline_min += $minmax["min"];
- } else {
- $low[] = $minmax["min"];
- }
-
- $inline_max += $minmax["max"];
- $iter->next();
- }
-
- if ($inline_max > 0) {
- $high[] = $inline_max;
- }
- if ($inline_min > 0) {
- $low[] = $inline_min;
- }
-
- if ($iter->valid()) {
- list($low[], $high[]) = $iter->current()->get_min_max_width();
- continue;
- }
- }
- $min = count($low) ? max($low) : 0;
- $max = count($high) ? max($high) : 0;
-
- // Use specified width if it is greater than the minimum defined by the
- // content. If the width is a percentage ignore it for now.
- $width = $style->width;
- if ($width !== "auto" && !Helpers::is_percent($width)) {
- $width = (float)$style->length_in_pt($width, $cb_w);
- if ($min < $width) {
- $min = $width;
- }
- if ($max < $width) {
- $max = $width;
- }
- }
-
- $min += $delta;
- $max += $delta;
- return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max);
- }
-
- /**
- * Parses a CSS string containing quotes and escaped hex characters
- *
- * @param $string string The CSS string to parse
- * @param $single_trim
- * @return string
- */
- protected function _parse_string($string, $single_trim = false)
- {
- if ($single_trim) {
- $string = preg_replace('/^[\"\']/', "", $string);
- $string = preg_replace('/[\"\']$/', "", $string);
- } else {
- $string = trim($string, "'\"");
- }
-
- $string = str_replace(array("\\\n", '\\"', "\\'"),
- array("", '"', "'"), $string);
-
- // Convert escaped hex characters into ascii characters (e.g. \A => newline)
- $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/",
- function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); },
- $string);
- return $string;
- }
-
- /**
- * Parses a CSS "quotes" property
- *
- * @return array|null An array of pairs of quotes
- */
- protected function _parse_quotes()
- {
- // Matches quote types
- $re = '/(\'[^\']*\')|(\"[^\"]*\")/';
-
- $quotes = $this->_frame->get_style()->quotes;
-
- // split on spaces, except within quotes
- if (!preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER)) {
- return null;
- }
-
- $quotes_array = array();
- foreach ($matches as $_quote) {
- $quotes_array[] = $this->_parse_string($_quote[0], true);
- }
-
- if (empty($quotes_array)) {
- $quotes_array = array('"', '"');
- }
-
- return array_chunk($quotes_array, 2);
- }
-
- /**
- * Parses the CSS "content" property
- *
- * @return string|null The resulting string
- */
- protected function _parse_content()
- {
- // Matches generated content
- $re = "/\n" .
- "\s(counters?\\([^)]*\\))|\n" .
- "\A(counters?\\([^)]*\\))|\n" .
- "\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?_frame->get_style()->content;
-
- $quotes = $this->_parse_quotes();
-
- // split on spaces, except within quotes
- if (!preg_match_all($re, $content, $matches, PREG_SET_ORDER)) {
- return null;
- }
-
- $text = "";
-
- foreach ($matches as $match) {
- if (isset($match[2]) && $match[2] !== "") {
- $match[1] = $match[2];
- }
-
- if (isset($match[6]) && $match[6] !== "") {
- $match[4] = $match[6];
- }
-
- if (isset($match[8]) && $match[8] !== "") {
- $match[7] = $match[8];
- }
-
- if (isset($match[1]) && $match[1] !== "") {
- // counters?(...)
- $match[1] = mb_strtolower(trim($match[1]));
-
- // Handle counter() references:
- // http://www.w3.org/TR/CSS21/generate.html#content
-
- $i = mb_strpos($match[1], ")");
- if ($i === false) {
- continue;
- }
-
- preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]*)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $match[1], $args);
- $counter_id = $args[3];
- if (strtolower($args[1]) == 'counter') {
- // counter(name [,style])
- if (isset($args[5])) {
- $type = trim($args[5]);
- } else {
- $type = null;
- }
- $p = $this->_frame->lookup_counter_frame($counter_id);
-
- $text .= $p->counter_value($counter_id, $type);
-
- } else if (strtolower($args[1]) == 'counters') {
- // counters(name, string [,style])
- if (isset($args[5])) {
- $string = $this->_parse_string($args[5]);
- } else {
- $string = "";
- }
-
- if (isset($args[7])) {
- $type = trim($args[7]);
- } else {
- $type = null;
- }
-
- $p = $this->_frame->lookup_counter_frame($counter_id);
- $tmp = array();
- while ($p) {
- // We only want to use the counter values when they actually increment the counter
- if (array_key_exists($counter_id, $p->_counters)) {
- array_unshift($tmp, $p->counter_value($counter_id, $type));
- }
- $p = $p->lookup_counter_frame($counter_id);
- }
- $text .= implode($string, $tmp);
- } else {
- // countertops?
- continue;
- }
-
- } else if (isset($match[4]) && $match[4] !== "") {
- // String match
- $text .= $this->_parse_string($match[4]);
- } else if (isset($match[7]) && $match[7] !== "") {
- // Directive match
-
- if ($match[7] === "open-quote") {
- // FIXME: do something here
- $text .= $quotes[0][0];
- } else if ($match[7] === "close-quote") {
- // FIXME: do something else here
- $text .= $quotes[0][1];
- } else if ($match[7] === "no-open-quote") {
- // FIXME:
- } else if ($match[7] === "no-close-quote") {
- // FIXME:
- } else if (mb_strpos($match[7], "attr(") === 0) {
- $i = mb_strpos($match[7], ")");
- if ($i === false) {
- continue;
- }
-
- $attr = mb_substr($match[7], 5, $i - 5);
- if ($attr == "") {
- continue;
- }
-
- $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
- } else {
- continue;
- }
- }
- }
-
- return $text;
- }
-
- /**
- * Sets the generated content of a generated frame
- */
- protected function _set_content()
- {
- $frame = $this->_frame;
- $style = $frame->get_style();
-
- // if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value
- if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") {
- $vars = preg_split('/\s+/', trim($reset), 2);
- $frame->reset_counter($vars[0], (isset($frame->_counters['__' . $vars[0]]) ? $frame->_counters['__' . $vars[0]] : (isset($vars[1]) ? $vars[1] : 0)));
- }
-
- if ($style->counter_increment && ($increment = $style->counter_increment) !== "none") {
- $frame->increment_counters($increment);
- }
-
- if ($style->content && $frame->get_node()->nodeName === "dompdf_generated") {
- $content = $this->_parse_content();
- // add generated content to the font subset
- // FIXME: This is currently too late because the font subset has already been generated.
- // See notes in issue #750.
- if ($frame->get_dompdf()->getOptions()->getIsFontSubsettingEnabled() && $frame->get_dompdf()->get_canvas() instanceof CPDF) {
- $frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content);
- }
-
- $node = $frame->get_node()->ownerDocument->createTextNode($content);
-
- $new_style = $style->get_stylesheet()->create_style();
- $new_style->inherit($style);
-
- $new_frame = new Frame($node);
- $new_frame->set_style($new_style);
-
- Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root());
- $frame->append_child($new_frame);
- }
- }
-
- /**
- * Determine current frame width based on contents
- *
- * @return float
- */
- public function calculate_auto_width()
- {
- return $this->_frame->get_margin_width();
- }
-}
diff --git a/library/vendor/dompdf/src/FrameReflower/Block.php b/library/vendor/dompdf/src/FrameReflower/Block.php
deleted file mode 100644
index c6b75eb01..000000000
--- a/library/vendor/dompdf/src/FrameReflower/Block.php
+++ /dev/null
@@ -1,948 +0,0 @@
-
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameReflower;
-
-use Dompdf\Frame;
-use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
-use Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator;
-use Dompdf\FrameDecorator\Text as TextFrameDecorator;
-use Dompdf\Exception;
-use Dompdf\Css\Style;
-
-/**
- * Reflows block frames
- *
- * @package dompdf
- */
-class Block extends AbstractFrameReflower
-{
- // Minimum line width to justify, as fraction of available width
- const MIN_JUSTIFY_WIDTH = 0.80;
-
- /**
- * @var BlockFrameDecorator
- */
- protected $_frame;
-
- function __construct(BlockFrameDecorator $frame)
- {
- parent::__construct($frame);
- }
-
- /**
- * Calculate the ideal used value for the width property as per:
- * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
- *
- * @param float $width
- *
- * @return array
- */
- protected function _calculate_width($width)
- {
- $frame = $this->_frame;
- $style = $frame->get_style();
- $w = $frame->get_containing_block("w");
-
- if ($style->position === "fixed") {
- $w = $frame->get_parent()->get_containing_block("w");
- }
-
- $rm = $style->length_in_pt($style->margin_right, $w);
- $lm = $style->length_in_pt($style->margin_left, $w);
-
- $left = $style->length_in_pt($style->left, $w);
- $right = $style->length_in_pt($style->right, $w);
-
- // Handle 'auto' values
- $dims = array($style->border_left_width,
- $style->border_right_width,
- $style->padding_left,
- $style->padding_right,
- $width !== "auto" ? $width : 0,
- $rm !== "auto" ? $rm : 0,
- $lm !== "auto" ? $lm : 0);
-
- // absolutely positioned boxes take the 'left' and 'right' properties into account
- if ($frame->is_absolute()) {
- $absolute = true;
- $dims[] = $left !== "auto" ? $left : 0;
- $dims[] = $right !== "auto" ? $right : 0;
- } else {
- $absolute = false;
- }
-
- $sum = (float)$style->length_in_pt($dims, $w);
-
- // Compare to the containing block
- $diff = $w - $sum;
-
- if ($diff > 0) {
- if ($absolute) {
- // resolve auto properties: see
- // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
-
- if ($width === "auto" && $left === "auto" && $right === "auto") {
- if ($lm === "auto") {
- $lm = 0;
- }
- if ($rm === "auto") {
- $rm = 0;
- }
-
- // Technically, the width should be "shrink-to-fit" i.e. based on the
- // preferred width of the content... a little too costly here as a
- // special case. Just get the width to take up the slack:
- $left = 0;
- $right = 0;
- $width = $diff;
- } else if ($width === "auto") {
- if ($lm === "auto") {
- $lm = 0;
- }
- if ($rm === "auto") {
- $rm = 0;
- }
- if ($left === "auto") {
- $left = 0;
- }
- if ($right === "auto") {
- $right = 0;
- }
-
- $width = $diff;
- } else if ($left === "auto") {
- if ($lm === "auto") {
- $lm = 0;
- }
- if ($rm === "auto") {
- $rm = 0;
- }
- if ($right === "auto") {
- $right = 0;
- }
-
- $left = $diff;
- } else if ($right === "auto") {
- if ($lm === "auto") {
- $lm = 0;
- }
- if ($rm === "auto") {
- $rm = 0;
- }
-
- $right = $diff;
- }
-
- } else {
- // Find auto properties and get them to take up the slack
- if ($width === "auto") {
- $width = $diff;
- } else if ($lm === "auto" && $rm === "auto") {
- $lm = $rm = round($diff / 2);
- } else if ($lm === "auto") {
- $lm = $diff;
- } else if ($rm === "auto") {
- $rm = $diff;
- }
- }
- } else if ($diff < 0) {
- // We are over constrained--set margin-right to the difference
- $rm = $diff;
- }
-
- return array(
- "width" => $width,
- "margin_left" => $lm,
- "margin_right" => $rm,
- "left" => $left,
- "right" => $right,
- );
- }
-
- /**
- * Call the above function, but resolve max/min widths
- *
- * @throws Exception
- * @return array
- */
- protected function _calculate_restricted_width()
- {
- $frame = $this->_frame;
- $style = $frame->get_style();
- $cb = $frame->get_containing_block();
-
- if ($style->position === "fixed") {
- $cb = $frame->get_root()->get_containing_block();
- }
-
- //if ( $style->position === "absolute" )
- // $cb = $frame->find_positionned_parent()->get_containing_block();
-
- if (!isset($cb["w"])) {
- throw new Exception("Box property calculation requires containing block width");
- }
-
- // Treat width 100% as auto
- if ($style->width === "100%") {
- $width = "auto";
- } else {
- $width = $style->length_in_pt($style->width, $cb["w"]);
- }
-
- $calculate_width = $this->_calculate_width($width);
- $margin_left = $calculate_width['margin_left'];
- $margin_right = $calculate_width['margin_right'];
- $width = $calculate_width['width'];
- $left = $calculate_width['left'];
- $right = $calculate_width['right'];
-
- // Handle min/max width
- $min_width = $style->length_in_pt($style->min_width, $cb["w"]);
- $max_width = $style->length_in_pt($style->max_width, $cb["w"]);
-
- if ($max_width !== "none" && $min_width > $max_width) {
- list($max_width, $min_width) = array($min_width, $max_width);
- }
-
- if ($max_width !== "none" && $width > $max_width) {
- extract($this->_calculate_width($max_width));
- }
-
- if ($width < $min_width) {
- $calculate_width = $this->_calculate_width($min_width);
- $margin_left = $calculate_width['margin_left'];
- $margin_right = $calculate_width['margin_right'];
- $width = $calculate_width['width'];
- $left = $calculate_width['left'];
- $right = $calculate_width['right'];
- }
-
- return array($width, $margin_left, $margin_right, $left, $right);
- }
-
- /**
- * Determine the unrestricted height of content within the block
- * not by adding each line's height, but by getting the last line's position.
- * This because lines could have been pushed lower by a clearing element.
- *
- * @return float
- */
- protected function _calculate_content_height()
- {
- $height = 0;
- $lines = $this->_frame->get_line_boxes();
- if (count($lines) > 0) {
- $last_line = end($lines);
- $content_box = $this->_frame->get_content_box();
- $height = $last_line->y + $last_line->h - $content_box["y"];
- }
- return $height;
- }
-
- /**
- * Determine the frame's restricted height
- *
- * @return array
- */
- protected function _calculate_restricted_height()
- {
- $frame = $this->_frame;
- $style = $frame->get_style();
- $content_height = $this->_calculate_content_height();
- $cb = $frame->get_containing_block();
-
- $height = $style->length_in_pt($style->height, $cb["h"]);
-
- $top = $style->length_in_pt($style->top, $cb["h"]);
- $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
-
- $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
- $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
-
- if ($frame->is_absolute()) {
-
- // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
-
- $dims = array($top !== "auto" ? $top : 0,
- $style->margin_top !== "auto" ? $style->margin_top : 0,
- $style->padding_top,
- $style->border_top_width,
- $height !== "auto" ? $height : 0,
- $style->border_bottom_width,
- $style->padding_bottom,
- $style->margin_bottom !== "auto" ? $style->margin_bottom : 0,
- $bottom !== "auto" ? $bottom : 0);
-
- $sum = (float)$style->length_in_pt($dims, $cb["h"]);
-
- $diff = $cb["h"] - $sum;
-
- if ($diff > 0) {
- if ($height === "auto" && $top === "auto" && $bottom === "auto") {
- if ($margin_top === "auto") {
- $margin_top = 0;
- }
- if ($margin_bottom === "auto") {
- $margin_bottom = 0;
- }
-
- $height = $diff;
- } else if ($height === "auto" && $top === "auto") {
- if ($margin_top === "auto") {
- $margin_top = 0;
- }
- if ($margin_bottom === "auto") {
- $margin_bottom = 0;
- }
-
- $height = $content_height;
- $top = $diff - $content_height;
- } else if ($height === "auto" && $bottom === "auto") {
- if ($margin_top === "auto") {
- $margin_top = 0;
- }
- if ($margin_bottom === "auto") {
- $margin_bottom = 0;
- }
-
- $height = $content_height;
- $bottom = $diff - $content_height;
- } else if ($top === "auto" && $bottom === "auto") {
- if ($margin_top === "auto") {
- $margin_top = 0;
- }
- if ($margin_bottom === "auto") {
- $margin_bottom = 0;
- }
-
- $bottom = $diff;
- } else if ($top === "auto") {
- if ($margin_top === "auto") {
- $margin_top = 0;
- }
- if ($margin_bottom === "auto") {
- $margin_bottom = 0;
- }
-
- $top = $diff;
- } else if ($height === "auto") {
- if ($margin_top === "auto") {
- $margin_top = 0;
- }
- if ($margin_bottom === "auto") {
- $margin_bottom = 0;
- }
-
- $height = $diff;
- } else if ($bottom === "auto") {
- if ($margin_top === "auto") {
- $margin_top = 0;
- }
- if ($margin_bottom === "auto") {
- $margin_bottom = 0;
- }
-
- $bottom = $diff;
- } else {
- if ($style->overflow === "visible") {
- // set all autos to zero
- if ($margin_top === "auto") {
- $margin_top = 0;
- }
- if ($margin_bottom === "auto") {
- $margin_bottom = 0;
- }
- if ($top === "auto") {
- $top = 0;
- }
- if ($bottom === "auto") {
- $bottom = 0;
- }
- if ($height === "auto") {
- $height = $content_height;
- }
- }
-
- // FIXME: overflow hidden
- }
- }
-
- } else {
- // Expand the height if overflow is visible
- if ($height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) {
- $height = $content_height;
- }
-
- // FIXME: this should probably be moved to a seperate function as per
- // _calculate_restricted_width
-
- // Only handle min/max height if the height is independent of the frame's content
- if (!($style->overflow === "visible" || ($style->overflow === "hidden" && $height === "auto"))) {
- $min_height = $style->min_height;
- $max_height = $style->max_height;
-
- if (isset($cb["h"])) {
- $min_height = $style->length_in_pt($min_height, $cb["h"]);
- $max_height = $style->length_in_pt($max_height, $cb["h"]);
- } else if (isset($cb["w"])) {
- if (mb_strpos($min_height, "%") !== false) {
- $min_height = 0;
- } else {
- $min_height = $style->length_in_pt($min_height, $cb["w"]);
- }
-
- if (mb_strpos($max_height, "%") !== false) {
- $max_height = "none";
- } else {
- $max_height = $style->length_in_pt($max_height, $cb["w"]);
- }
- }
-
- if ($max_height !== "none" && $min_height > $max_height) {
- // Swap 'em
- list($max_height, $min_height) = array($min_height, $max_height);
- }
-
- if ($max_height !== "none" && $height > $max_height) {
- $height = $max_height;
- }
-
- if ($height < $min_height) {
- $height = $min_height;
- }
- }
- }
-
- return array($height, $margin_top, $margin_bottom, $top, $bottom);
- }
-
- /**
- * Adjust the justification of each of our lines.
- * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
- */
- protected function _text_align()
- {
- $style = $this->_frame->get_style();
- $w = $this->_frame->get_containing_block("w");
- $width = (float)$style->length_in_pt($style->width, $w);
-
- switch ($style->text_align) {
- default:
- case "left":
- foreach ($this->_frame->get_line_boxes() as $line) {
- if (!$line->left) {
- continue;
- }
-
- foreach ($line->get_frames() as $frame) {
- if ($frame instanceof BlockFrameDecorator) {
- continue;
- }
- $frame->set_position($frame->get_position("x") + $line->left);
- }
- }
- return;
-
- case "right":
- foreach ($this->_frame->get_line_boxes() as $line) {
- // Move each child over by $dx
- $dx = $width - $line->w - $line->right;
-
- foreach ($line->get_frames() as $frame) {
- // Block frames are not aligned by text-align
- if ($frame instanceof BlockFrameDecorator) {
- continue;
- }
-
- $frame->set_position($frame->get_position("x") + $dx);
- }
- }
- break;
-
- case "justify":
- // We justify all lines except the last one
- $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards)
- $last_line = array_pop($lines);
-
- foreach ($lines as $i => $line) {
- if ($line->br) {
- unset($lines[$i]);
- }
- }
-
- // One space character's width. Will be used to get a more accurate spacing
- $space_width = $this->get_dompdf()->getFontMetrics()->getTextWidth(" ", $style->font_family, $style->font_size);
-
- foreach ($lines as $line) {
- if ($line->left) {
- foreach ($line->get_frames() as $frame) {
- if (!$frame instanceof TextFrameDecorator) {
- continue;
- }
-
- $frame->set_position($frame->get_position("x") + $line->left);
- }
- }
-
- // Set the spacing for each child
- if ($line->wc > 1) {
- $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1);
- } else {
- $spacing = 0;
- }
-
- $dx = 0;
- foreach ($line->get_frames() as $frame) {
- if (!$frame instanceof TextFrameDecorator) {
- continue;
- }
-
- $text = $frame->get_text();
- $spaces = mb_substr_count($text, " ");
-
- $char_spacing = (float)$style->length_in_pt($style->letter_spacing);
- $_spacing = $spacing + $char_spacing;
-
- $frame->set_position($frame->get_position("x") + $dx);
- $frame->set_text_spacing($_spacing);
-
- $dx += $spaces * $_spacing;
- }
-
- // The line (should) now occupy the entire width
- $line->w = $width;
- }
-
- // Adjust the last line if necessary
- if ($last_line->left) {
- foreach ($last_line->get_frames() as $frame) {
- if ($frame instanceof BlockFrameDecorator) {
- continue;
- }
- $frame->set_position($frame->get_position("x") + $last_line->left);
- }
- }
- break;
-
- case "center":
- case "centre":
- foreach ($this->_frame->get_line_boxes() as $line) {
- // Centre each line by moving each frame in the line by:
- $dx = ($width + $line->left - $line->w - $line->right) / 2;
-
- foreach ($line->get_frames() as $frame) {
- // Block frames are not aligned by text-align
- if ($frame instanceof BlockFrameDecorator) {
- continue;
- }
-
- $frame->set_position($frame->get_position("x") + $dx);
- }
- }
- break;
- }
- }
-
- /**
- * Align inline children vertically.
- * Aligns each child vertically after each line is reflowed
- */
- function vertical_align()
- {
- $canvas = null;
-
- foreach ($this->_frame->get_line_boxes() as $line) {
-
- $height = $line->h;
-
- foreach ($line->get_frames() as $frame) {
- $style = $frame->get_style();
- $isInlineBlock = (
- '-dompdf-image' === $style->display
- || 'inline-block' === $style->display
- || 'inline-table' === $style->display
- );
- if (!$isInlineBlock && $style->display !== "inline") {
- continue;
- }
-
- if (!isset($canvas)) {
- $canvas = $frame->get_root()->get_dompdf()->get_canvas();
- }
-
- $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size);
- $y_offset = 0;
-
- //FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?)
- if($isInlineBlock) {
- $lineFrames = $line->get_frames();
- if (count($lineFrames) == 1) {
- continue;
- }
- $frameBox = $frame->get_frame()->get_border_box();
- $imageHeightDiff = $height * 0.8 - (float)$frameBox['h'];
-
- $align = $frame->get_style()->vertical_align;
- if (in_array($align, Style::$vertical_align_keywords) === true) {
- switch ($align) {
- case "middle":
- $y_offset = $imageHeightDiff / 2;
- break;
-
- case "sub":
- $y_offset = 0.3 * $height + $imageHeightDiff;
- break;
-
- case "super":
- $y_offset = -0.2 * $height + $imageHeightDiff;
- break;
-
- case "text-top": // FIXME: this should be the height of the frame minus the height of the text
- $y_offset = $height - (float)$style->length_in_pt($style->line_height, $style->font_size);
- break;
-
- case "top":
- break;
-
- case "text-bottom": // FIXME: align bottom of image with the descender?
- case "bottom":
- $y_offset = 0.3 * $height + $imageHeightDiff;
- break;
-
- case "baseline":
- default:
- $y_offset = $imageHeightDiff;
- break;
- }
- } else {
- $y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - (float)$frameBox['h'];
- }
- } else {
- $parent = $frame->get_parent();
- if ($parent instanceof TableCellFrameDecorator) {
- $align = "baseline";
- } else {
- $align = $parent->get_style()->vertical_align;
- }
- if (in_array($align, Style::$vertical_align_keywords) === true) {
- switch ($align) {
- case "middle":
- $y_offset = ($height * 0.8 - $baseline) / 2;
- break;
-
- case "sub":
- $y_offset = $height * 0.8 - $baseline * 0.5;
- break;
-
- case "super":
- $y_offset = $height * 0.8 - $baseline * 1.4;
- break;
-
- case "text-top":
- case "top": // Not strictly accurate, but good enough for now
- break;
-
- case "text-bottom":
- case "bottom":
- $y_offset = $height * 0.8 - $baseline;
- break;
-
- case "baseline":
- default:
- $y_offset = $height * 0.8 - $baseline;
- break;
- }
- } else {
- $y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size);
- }
- }
-
- if ($y_offset !== 0) {
- $frame->move(0, $y_offset);
- }
- }
- }
- }
-
- /**
- * @param Frame $child
- */
- function process_clear(Frame $child)
- {
- $child_style = $child->get_style();
- $root = $this->_frame->get_root();
-
- // Handle "clear"
- if ($child_style->clear !== "none") {
- //TODO: this is a WIP for handling clear/float frames that are in between inline frames
- if ($child->get_prev_sibling() !== null) {
- $this->_frame->add_line();
- }
- if ($child_style->float !== "none" && $child->get_next_sibling()) {
- $this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1);
- }
-
- $lowest_y = $root->get_lowest_float_offset($child);
-
- // If a float is still applying, we handle it
- if ($lowest_y) {
- if ($child->is_in_flow()) {
- $line_box = $this->_frame->get_current_line_box();
- $line_box->y = $lowest_y + $child->get_margin_height();
- $line_box->left = 0;
- $line_box->right = 0;
- }
-
- $child->move(0, $lowest_y - $child->get_position("y"));
- }
- }
- }
-
- /**
- * @param Frame $child
- * @param float $cb_x
- * @param float $cb_w
- */
- function process_float(Frame $child, $cb_x, $cb_w)
- {
- $child_style = $child->get_style();
- $root = $this->_frame->get_root();
-
- // Handle "float"
- if ($child_style->float !== "none") {
- $root->add_floating_frame($child);
-
- // Remove next frame's beginning whitespace
- $next = $child->get_next_sibling();
- if ($next && $next instanceof TextFrameDecorator) {
- $next->set_text(ltrim($next->get_text()));
- }
-
- $line_box = $this->_frame->get_current_line_box();
- list($old_x, $old_y) = $child->get_position();
-
- $float_x = $cb_x;
- $float_y = $old_y;
- $float_w = $child->get_margin_width();
-
- if ($child_style->clear === "none") {
- switch ($child_style->float) {
- case "left":
- $float_x += $line_box->left;
- break;
- case "right":
- $float_x += ($cb_w - $line_box->right - $float_w);
- break;
- }
- } else {
- if ($child_style->float === "right") {
- $float_x += ($cb_w - $float_w);
- }
- }
-
- if ($cb_w < $float_x + $float_w - $old_x) {
- // TODO handle when floating elements don't fit
- }
-
- $line_box->get_float_offsets();
-
- if ($child->_float_next_line) {
- $float_y += $line_box->h;
- }
-
- $child->set_position($float_x, $float_y);
- $child->move($float_x - $old_x, $float_y - $old_y, true);
- }
- }
-
- /**
- * @param BlockFrameDecorator $block
- * @return mixed|void
- */
- function reflow(BlockFrameDecorator $block = null)
- {
-
- // Check if a page break is forced
- $page = $this->_frame->get_root();
- $page->check_forced_page_break($this->_frame);
-
- // Bail if the page is full
- if ($page->is_full()) {
- return;
- }
-
- // Generated content
- $this->_set_content();
-
- // Collapse margins if required
- $this->_collapse_margins();
-
- $style = $this->_frame->get_style();
- $cb = $this->_frame->get_containing_block();
-
- if ($style->position === "fixed") {
- $cb = $this->_frame->get_root()->get_containing_block();
- }
-
- // Determine the constraints imposed by this frame: calculate the width
- // of the content area:
- list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
-
- // Store the calculated properties
- $style->width = $w;
- $style->margin_left = $left_margin;
- $style->margin_right = $right_margin;
- $style->left = $left;
- $style->right = $right;
-
- // Update the position
- $this->_frame->position();
- list($x, $y) = $this->_frame->get_position();
-
- // Adjust the first line based on the text-indent property
- $indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]);
- $this->_frame->increase_line_width($indent);
-
- // Determine the content edge
- $top = (float)$style->length_in_pt(array($style->margin_top,
- $style->padding_top,
- $style->border_top_width), $cb["h"]);
-
- $bottom = (float)$style->length_in_pt(array($style->border_bottom_width,
- $style->margin_bottom,
- $style->padding_bottom), $cb["h"]);
-
- $cb_x = $x + (float)$left_margin + (float)$style->length_in_pt(array($style->border_left_width,
- $style->padding_left), $cb["w"]);
-
- $cb_y = $y + $top;
-
- $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
-
- // Set the y position of the first line in this block
- $line_box = $this->_frame->get_current_line_box();
- $line_box->y = $cb_y;
- $line_box->get_float_offsets();
-
- // Set the containing blocks and reflow each child
- foreach ($this->_frame->get_children() as $child) {
-
- // Bail out if the page is full
- if ($page->is_full()) {
- break;
- }
-
- $child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
-
- $this->process_clear($child);
-
- $child->reflow($this->_frame);
-
- // Don't add the child to the line if a page break has occurred
- if ($page->check_page_break($child)) {
- break;
- }
-
- $this->process_float($child, $cb_x, $w);
- }
-
- // Determine our height
- list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
- $style->height = $height;
- $style->margin_top = $margin_top;
- $style->margin_bottom = $margin_bottom;
- $style->top = $top;
- $style->bottom = $bottom;
-
- $orig_style = $this->_frame->get_original_style();
-
- $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"));
-
- // Absolute positioning measurement
- if ($needs_reposition) {
- if ($orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto")) {
- $width = 0;
- foreach ($this->_frame->get_line_boxes() as $line) {
- $width = max($line->w, $width);
- }
- $style->width = $width;
- }
-
- $style->left = $orig_style->left;
- $style->right = $orig_style->right;
- }
-
- // Calculate inline-block / float auto-widths
- if (($style->display === "inline-block" || $style->float !== 'none') && $orig_style->width === 'auto') {
- $width = 0;
-
- foreach ($this->_frame->get_line_boxes() as $line) {
- $line->recalculate_width();
-
- $width = max($line->w, $width);
- }
-
- if ($width === 0) {
- foreach ($this->_frame->get_children() as $child) {
- $width += $child->calculate_auto_width();
- }
- }
-
- $style->width = $width;
- }
-
- $this->_text_align();
- $this->vertical_align();
-
- // Absolute positioning
- if ($needs_reposition) {
- list($x, $y) = $this->_frame->get_position();
- $this->_frame->position();
- list($new_x, $new_y) = $this->_frame->get_position();
- $this->_frame->move($new_x - $x, $new_y - $y, true);
- }
-
- if ($block && $this->_frame->is_in_flow()) {
- $block->add_frame_to_line($this->_frame);
-
- // May be inline-block
- if ($style->display === "block") {
- $block->add_line();
- }
- }
- }
-
- /**
- * Determine current frame width based on contents
- *
- * @return float
- */
- public function calculate_auto_width()
- {
- $width = 0;
-
- foreach ($this->_frame->get_line_boxes() as $line) {
- $line_width = 0;
-
- foreach ($line->get_frames() as $frame) {
- if ($frame->get_original_style()->width == 'auto') {
- $line_width += $frame->calculate_auto_width();
- } else {
- $line_width += $frame->get_margin_width();
- }
- }
-
- $width = max($line_width, $width);
- }
-
- $this->_frame->get_style()->width = $width;
-
- return $this->_frame->get_margin_width();
- }
-}
diff --git a/library/vendor/dompdf/src/FrameReflower/Image.php b/library/vendor/dompdf/src/FrameReflower/Image.php
deleted file mode 100644
index 805523c52..000000000
--- a/library/vendor/dompdf/src/FrameReflower/Image.php
+++ /dev/null
@@ -1,206 +0,0 @@
-
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameReflower;
-
-use Dompdf\Helpers;
-use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
-use Dompdf\FrameDecorator\Image as ImageFrameDecorator;
-
-/**
- * Image reflower class
- *
- * @package dompdf
- */
-class Image extends AbstractFrameReflower
-{
-
- /**
- * Image constructor.
- * @param ImageFrameDecorator $frame
- */
- function __construct(ImageFrameDecorator $frame)
- {
- parent::__construct($frame);
- }
-
- /**
- * @param BlockFrameDecorator|null $block
- */
- function reflow(BlockFrameDecorator $block = null)
- {
- $this->_frame->position();
-
- //FLOAT
- //$frame = $this->_frame;
- //$page = $frame->get_root();
-
- //if ($frame->get_style()->float !== "none" ) {
- // $page->add_floating_frame($this);
- //}
-
- // Set the frame's width
- $this->get_min_max_width();
-
- if ($block) {
- $block->add_frame_to_line($this->_frame);
- }
- }
-
- /**
- * @return array
- */
- function get_min_max_width()
- {
- if ($this->get_dompdf()->getOptions()->getDebugPng()) {
- // Determine the image's size. Time consuming. Only when really needed?
- list($img_width, $img_height) = Helpers::dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->getHttpContext());
- print "get_min_max_width() " .
- $this->_frame->get_style()->width . ' ' .
- $this->_frame->get_style()->height . ';' .
- $this->_frame->get_parent()->get_style()->width . " " .
- $this->_frame->get_parent()->get_style()->height . ";" .
- $this->_frame->get_parent()->get_parent()->get_style()->width . ' ' .
- $this->_frame->get_parent()->get_parent()->get_style()->height . ';' .
- $img_width . ' ' .
- $img_height . '|';
- }
-
- $style = $this->_frame->get_style();
-
- $width_forced = true;
- $height_forced = true;
-
- //own style auto or invalid value: use natural size in px
- //own style value: ignore suffix text including unit, use given number as px
- //own style %: walk up parent chain until found available space in pt; fill available space
- //
- //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto
-
- $width = ($style->width > 0 ? $style->width : 0);
- if (Helpers::is_percent($width)) {
- $t = 0.0;
- for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
- $f_style = $f->get_style();
- $t = $f_style->length_in_pt($f_style->width);
- if ($t != 0) {
- break;
- }
- }
- $width = ((float)rtrim($width, "%") * $t) / 100; //maybe 0
- } else {
- // Don't set image original size if "%" branch was 0 or size not given.
- // Otherwise aspect changed on %/auto combination for width/height
- // Resample according to px per inch
- // See also ListBulletImage::__construct
- $width = $style->length_in_pt($width);
- }
-
- $height = ($style->height > 0 ? $style->height : 0);
- if (Helpers::is_percent($height)) {
- $t = 0.0;
- for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
- $f_style = $f->get_style();
- $t = (float)$f_style->length_in_pt($f_style->height);
- if ($t != 0) {
- break;
- }
- }
- $height = ((float)rtrim($height, "%") * $t) / 100; //maybe 0
- } else {
- // Don't set image original size if "%" branch was 0 or size not given.
- // Otherwise aspect changed on %/auto combination for width/height
- // Resample according to px per inch
- // See also ListBulletImage::__construct
- $height = $style->length_in_pt($height);
- }
-
- if ($width == 0 || $height == 0) {
- // Determine the image's size. Time consuming. Only when really needed!
- list($img_width, $img_height) = Helpers::dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->getHttpContext());
-
- // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable.
- // Resample according to px per inch
- // See also ListBulletImage::__construct
- if ($width == 0 && $height == 0) {
- $dpi = $this->_frame->get_dompdf()->getOptions()->getDpi();
- $width = (float)($img_width * 72) / $dpi;
- $height = (float)($img_height * 72) / $dpi;
- $width_forced = false;
- $height_forced = false;
- } elseif ($height == 0 && $width != 0) {
- $height_forced = false;
- $height = ($width / $img_width) * $img_height; //keep aspect ratio
- } elseif ($width == 0 && $height != 0) {
- $width_forced = false;
- $width = ($height / $img_height) * $img_width; //keep aspect ratio
- }
- }
-
- // Handle min/max width/height
- if ($style->min_width !== "none" ||
- $style->max_width !== "none" ||
- $style->min_height !== "none" ||
- $style->max_height !== "none"
- ) {
-
- list( /*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block();
-
- $min_width = $style->length_in_pt($style->min_width, $w);
- $max_width = $style->length_in_pt($style->max_width, $w);
- $min_height = $style->length_in_pt($style->min_height, $h);
- $max_height = $style->length_in_pt($style->max_height, $h);
-
- if ($max_width !== "none" && $width > $max_width) {
- if (!$height_forced) {
- $height *= $max_width / $width;
- }
-
- $width = $max_width;
- }
-
- if ($min_width !== "none" && $width < $min_width) {
- if (!$height_forced) {
- $height *= $min_width / $width;
- }
-
- $width = $min_width;
- }
-
- if ($max_height !== "none" && $height > $max_height) {
- if (!$width_forced) {
- $width *= $max_height / $height;
- }
-
- $height = $max_height;
- }
-
- if ($min_height !== "none" && $height < $min_height) {
- if (!$width_forced) {
- $width *= $min_height / $height;
- }
-
- $height = $min_height;
- }
- }
-
- if ($this->get_dompdf()->getOptions()->getDebugPng()) {
- print $width . ' ' . $height . ';';
- }
-
- $style->width = $width . "pt";
- $style->height = $height . "pt";
-
- $style->min_width = "none";
- $style->max_width = "none";
- $style->min_height = "none";
- $style->max_height = "none";
-
- return array($width, $width, "min" => $width, "max" => $width);
- }
-}
diff --git a/library/vendor/dompdf/src/FrameReflower/Inline.php b/library/vendor/dompdf/src/FrameReflower/Inline.php
deleted file mode 100644
index 68662a574..000000000
--- a/library/vendor/dompdf/src/FrameReflower/Inline.php
+++ /dev/null
@@ -1,103 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameReflower;
-
-use Dompdf\Frame;
-use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
-use Dompdf\FrameDecorator\Text as TextFrameDecorator;
-
-/**
- * Reflows inline frames
- *
- * @package dompdf
- */
-class Inline extends AbstractFrameReflower
-{
-
- /**
- * Inline constructor.
- * @param Frame $frame
- */
- function __construct(Frame $frame)
- {
- parent::__construct($frame);
- }
-
- /**
- * @param BlockFrameDecorator|null $block
- */
- function reflow(BlockFrameDecorator $block = null)
- {
- $frame = $this->_frame;
-
- // Check if a page break is forced
- $page = $frame->get_root();
- $page->check_forced_page_break($frame);
-
- if ($page->is_full()) {
- return;
- }
-
- $style = $frame->get_style();
-
- // Generated content
- $this->_set_content();
-
- $frame->position();
-
- $cb = $frame->get_containing_block();
-
- // Add our margin, padding & border to the first and last children
- if (($f = $frame->get_first_child()) && $f instanceof TextFrameDecorator) {
- $f_style = $f->get_style();
- $f_style->margin_left = $style->margin_left;
- $f_style->padding_left = $style->padding_left;
- $f_style->border_left = $style->border_left;
- }
-
- if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) {
- $l_style = $l->get_style();
- $l_style->margin_right = $style->margin_right;
- $l_style->padding_right = $style->padding_right;
- $l_style->border_right = $style->border_right;
- }
-
- if ($block) {
- $block->add_frame_to_line($this->_frame);
- }
-
- // Set the containing blocks and reflow each child. The containing
- // block is not changed by line boxes.
- foreach ($frame->get_children() as $child) {
- $child->set_containing_block($cb);
- $child->reflow($block);
- }
- }
-
- /**
- * Determine current frame width based on contents
- *
- * @return float
- */
- public function calculate_auto_width()
- {
- $width = 0;
-
- foreach ($this->_frame->get_children() as $child) {
- if ($child->get_original_style()->width == 'auto') {
- $width += $child->calculate_auto_width();
- } else {
- $width += $child->get_margin_width();
- }
- }
-
- $this->_frame->get_style()->width = $width;
-
- return $this->_frame->get_margin_width();
- }
-}
diff --git a/library/vendor/dompdf/src/FrameReflower/Table.php b/library/vendor/dompdf/src/FrameReflower/Table.php
deleted file mode 100644
index 1f560c053..000000000
--- a/library/vendor/dompdf/src/FrameReflower/Table.php
+++ /dev/null
@@ -1,589 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameReflower;
-
-use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
-use Dompdf\FrameDecorator\Table as TableFrameDecorator;
-
-/**
- * Reflows tables
- *
- * @access private
- * @package dompdf
- */
-class Table extends AbstractFrameReflower
-{
- /**
- * Frame for this reflower
- *
- * @var TableFrameDecorator
- */
- protected $_frame;
-
- /**
- * Cache of results between call to get_min_max_width and assign_widths
- *
- * @var array
- */
- protected $_state;
-
- /**
- * Table constructor.
- * @param TableFrameDecorator $frame
- */
- function __construct(TableFrameDecorator $frame)
- {
- $this->_state = null;
- parent::__construct($frame);
- }
-
- /**
- * State is held here so it needs to be reset along with the decorator
- */
- function reset()
- {
- $this->_state = null;
- $this->_min_max_cache = null;
- }
-
- protected function _assign_widths()
- {
- $style = $this->_frame->get_style();
-
- // Find the min/max width of the table and sort the columns into
- // absolute/percent/auto arrays
- $min_width = $this->_state["min_width"];
- $max_width = $this->_state["max_width"];
- $percent_used = $this->_state["percent_used"];
- $absolute_used = $this->_state["absolute_used"];
- $auto_min = $this->_state["auto_min"];
-
- $absolute =& $this->_state["absolute"];
- $percent =& $this->_state["percent"];
- $auto =& $this->_state["auto"];
-
- // Determine the actual width of the table
- $cb = $this->_frame->get_containing_block();
- $columns =& $this->_frame->get_cellmap()->get_columns();
-
- $width = $style->width;
-
- // Calculate padding & border fudge factor
- $left = $style->margin_left;
- $right = $style->margin_right;
-
- $centered = ($left === "auto" && $right === "auto");
-
- $left = (float)($left === "auto" ? 0 : $style->length_in_pt($left, $cb["w"]));
- $right = (float)($right === "auto" ? 0 : $style->length_in_pt($right, $cb["w"]));
-
- $delta = $left + $right;
-
- if (!$centered) {
- $delta += (float)$style->length_in_pt(array(
- $style->padding_left,
- $style->border_left_width,
- $style->border_right_width,
- $style->padding_right),
- $cb["w"]);
- }
-
- $min_table_width = (float)$style->length_in_pt($style->min_width, $cb["w"] - $delta);
-
- // min & max widths already include borders & padding
- $min_width -= $delta;
- $max_width -= $delta;
-
- if ($width !== "auto") {
- $preferred_width = (float)$style->length_in_pt($width, $cb["w"]) - $delta;
-
- if ($preferred_width < $min_table_width) {
- $preferred_width = $min_table_width;
- }
-
- if ($preferred_width > $min_width) {
- $width = $preferred_width;
- } else {
- $width = $min_width;
- }
-
- } else {
- if ($max_width + $delta < $cb["w"]) {
- $width = $max_width;
- } else if ($cb["w"] - $delta > $min_width) {
- $width = $cb["w"] - $delta;
- } else {
- $width = $min_width;
- }
-
- if ($width < $min_table_width) {
- $width = $min_table_width;
- }
-
- }
-
- // Store our resolved width
- $style->width = $width;
-
- $cellmap = $this->_frame->get_cellmap();
-
- if ($cellmap->is_columns_locked()) {
- return;
- }
-
- // If the whole table fits on the page, then assign each column it's max width
- if ($width == $max_width) {
- foreach (array_keys($columns) as $i) {
- $cellmap->set_column_width($i, $columns[$i]["max-width"]);
- }
-
- return;
- }
-
- // Determine leftover and assign it evenly to all columns
- if ($width > $min_width) {
- // We have four cases to deal with:
- //
- // 1. All columns are auto--no widths have been specified. In this
- // case we distribute extra space across all columns weighted by max-width.
- //
- // 2. Only absolute widths have been specified. In this case we
- // distribute any extra space equally among 'width: auto' columns, or all
- // columns if no auto columns have been specified.
- //
- // 3. Only percentage widths have been specified. In this case we
- // normalize the percentage values and distribute any remaining % to
- // width: auto columns. We then proceed to assign widths as fractions
- // of the table width.
- //
- // 4. Both absolute and percentage widths have been specified.
-
- $increment = 0;
-
- // Case 1:
- if ($absolute_used == 0 && $percent_used == 0) {
- $increment = $width - $min_width;
-
- foreach (array_keys($columns) as $i) {
- $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment * ($columns[$i]["max-width"] / $max_width));
- }
- return;
- }
-
- // Case 2
- if ($absolute_used > 0 && $percent_used == 0) {
- if (count($auto) > 0) {
- $increment = ($width - $auto_min - $absolute_used) / count($auto);
- }
-
- // Use the absolutely specified width or the increment
- foreach (array_keys($columns) as $i) {
- if ($columns[$i]["absolute"] > 0 && count($auto)) {
- $cellmap->set_column_width($i, $columns[$i]["min-width"]);
- } else if (count($auto)) {
- $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
- } else {
- // All absolute columns
- $increment = ($width - $absolute_used) * $columns[$i]["absolute"] / $absolute_used;
-
- $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
- }
-
- }
- return;
- }
-
- // Case 3:
- if ($absolute_used == 0 && $percent_used > 0) {
- $scale = null;
- $remaining = null;
-
- // Scale percent values if the total percentage is > 100, or if all
- // values are specified as percentages.
- if ($percent_used > 100 || count($auto) == 0) {
- $scale = 100 / $percent_used;
- } else {
- $scale = 1;
- }
-
- // Account for the minimum space used by the unassigned auto columns
- $used_width = $auto_min;
-
- foreach ($percent as $i) {
- $columns[$i]["percent"] *= $scale;
-
- $slack = $width - $used_width;
-
- $w = min($columns[$i]["percent"] * $width / 100, $slack);
-
- if ($w < $columns[$i]["min-width"]) {
- $w = $columns[$i]["min-width"];
- }
-
- $cellmap->set_column_width($i, $w);
- $used_width += $w;
-
- }
-
- // This works because $used_width includes the min-width of each
- // unassigned column
- if (count($auto) > 0) {
- $increment = ($width - $used_width) / count($auto);
-
- foreach ($auto as $i) {
- $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
- }
-
- }
- return;
- }
-
- // Case 4:
-
- // First-come, first served
- if ($absolute_used > 0 && $percent_used > 0) {
- $used_width = $auto_min;
-
- foreach ($absolute as $i) {
- $cellmap->set_column_width($i, $columns[$i]["min-width"]);
- $used_width += $columns[$i]["min-width"];
- }
-
- // Scale percent values if the total percentage is > 100 or there
- // are no auto values to take up slack
- if ($percent_used > 100 || count($auto) == 0) {
- $scale = 100 / $percent_used;
- } else {
- $scale = 1;
- }
-
- $remaining_width = $width - $used_width;
-
- foreach ($percent as $i) {
- $slack = $remaining_width - $used_width;
-
- $columns[$i]["percent"] *= $scale;
- $w = min($columns[$i]["percent"] * $remaining_width / 100, $slack);
-
- if ($w < $columns[$i]["min-width"]) {
- $w = $columns[$i]["min-width"];
- }
-
- $columns[$i]["used-width"] = $w;
- $used_width += $w;
- }
-
- if (count($auto) > 0) {
- $increment = ($width - $used_width) / count($auto);
-
- foreach ($auto as $i) {
- $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
- }
- }
-
- return;
- }
- } else { // we are over constrained
- // Each column gets its minimum width
- foreach (array_keys($columns) as $i) {
- $cellmap->set_column_width($i, $columns[$i]["min-width"]);
- }
- }
- }
-
- /**
- * Determine the frame's height based on min/max height
- *
- * @return float|int|mixed|string
- */
- protected function _calculate_height()
- {
- $style = $this->_frame->get_style();
- $height = $style->height;
-
- $cellmap = $this->_frame->get_cellmap();
- $cellmap->assign_frame_heights();
- $rows = $cellmap->get_rows();
-
- // Determine our content height
- $content_height = 0;
- foreach ($rows as $r) {
- $content_height += $r["height"];
- }
-
- $cb = $this->_frame->get_containing_block();
-
- if (!($style->overflow === "visible" ||
- ($style->overflow === "hidden" && $height === "auto"))
- ) {
- // Only handle min/max height if the height is independent of the frame's content
-
- $min_height = $style->min_height;
- $max_height = $style->max_height;
-
- if (isset($cb["h"])) {
- $min_height = $style->length_in_pt($min_height, $cb["h"]);
- $max_height = $style->length_in_pt($max_height, $cb["h"]);
-
- } else if (isset($cb["w"])) {
- if (mb_strpos($min_height, "%") !== false) {
- $min_height = 0;
- } else {
- $min_height = $style->length_in_pt($min_height, $cb["w"]);
- }
- if (mb_strpos($max_height, "%") !== false) {
- $max_height = "none";
- } else {
- $max_height = $style->length_in_pt($max_height, $cb["w"]);
- }
- }
-
- if ($max_height !== "none" && $min_height > $max_height) {
- // Swap 'em
- list($max_height, $min_height) = array($min_height, $max_height);
- }
-
- if ($max_height !== "none" && $height > $max_height) {
- $height = $max_height;
- }
-
- if ($height < $min_height) {
- $height = $min_height;
- }
- } else {
- // Use the content height or the height value, whichever is greater
- if ($height !== "auto") {
- $height = $style->length_in_pt($height, $cb["h"]);
-
- if ($height <= $content_height) {
- $height = $content_height;
- } else {
- $cellmap->set_frame_heights($height, $content_height);
- }
- } else {
- $height = $content_height;
- }
- }
-
- return $height;
- }
-
- /**
- * @param BlockFrameDecorator $block
- */
- function reflow(BlockFrameDecorator $block = null)
- {
- /** @var TableFrameDecorator */
- $frame = $this->_frame;
-
- // Check if a page break is forced
- $page = $frame->get_root();
- $page->check_forced_page_break($frame);
-
- // Bail if the page is full
- if ($page->is_full()) {
- return;
- }
-
- // Let the page know that we're reflowing a table so that splits
- // are suppressed (simply setting page-break-inside: avoid won't
- // work because we may have an arbitrary number of block elements
- // inside tds.)
- $page->table_reflow_start();
-
- // Collapse vertical margins, if required
- $this->_collapse_margins();
-
- $frame->position();
-
- // Table layout algorithm:
- // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
-
- if (is_null($this->_state)) {
- $this->get_min_max_width();
- }
-
- $cb = $frame->get_containing_block();
- $style = $frame->get_style();
-
- // This is slightly inexact, but should be okay. Add half the
- // border-spacing to the table as padding. The other half is added to
- // the cells themselves.
- if ($style->border_collapse === "separate") {
- list($h, $v) = $style->border_spacing;
-
- $v = (float)$style->length_in_pt($v) / 2;
- $h = (float)$style->length_in_pt($h) / 2;
-
- $style->padding_left = (float)$style->length_in_pt($style->padding_left, $cb["w"]) + $h;
- $style->padding_right = (float)$style->length_in_pt($style->padding_right, $cb["w"]) + $h;
- $style->padding_top = (float)$style->length_in_pt($style->padding_top, $cb["h"]) + $v;
- $style->padding_bottom = (float)$style->length_in_pt($style->padding_bottom, $cb["h"]) + $v;
- }
-
- $this->_assign_widths();
-
- // Adjust left & right margins, if they are auto
- $width = $style->width;
- $left = $style->margin_left;
- $right = $style->margin_right;
-
- $diff = $cb["w"] - $width;
-
- if ($left === "auto" && $right === "auto") {
- if ($diff < 0) {
- $left = 0;
- $right = $diff;
- } else {
- $left = $right = $diff / 2;
- }
-
- $style->margin_left = sprintf("%Fpt", $left);
- $style->margin_right = sprintf("%Fpt", $right);;
- } else {
- if ($left === "auto") {
- $left = (float)$style->length_in_pt($cb["w"], $cb["w"]) - (float)$style->length_in_pt($right, $cb["w"]) - (float)$style->length_in_pt($width, $cb["w"]);
- }
- if ($right === "auto") {
- $left = (float)$style->length_in_pt($left, $cb["w"]);
- }
- }
-
- list($x, $y) = $frame->get_position();
-
- // Determine the content edge
- $content_x = $x + (float)$left + (float)$style->length_in_pt(array($style->padding_left,
- $style->border_left_width), $cb["w"]);
- $content_y = $y + (float)$style->length_in_pt(array($style->margin_top,
- $style->border_top_width,
- $style->padding_top), $cb["h"]);
-
- if (isset($cb["h"])) {
- $h = $cb["h"];
- } else {
- $h = null;
- }
-
- $cellmap = $frame->get_cellmap();
- $col =& $cellmap->get_column(0);
- $col["x"] = $content_x;
-
- $row =& $cellmap->get_row(0);
- $row["y"] = $content_y;
-
- $cellmap->assign_x_positions();
-
- // Set the containing block of each child & reflow
- foreach ($frame->get_children() as $child) {
- // Bail if the page is full
- if (!$page->in_nested_table() && $page->is_full()) {
- break;
- }
-
- $child->set_containing_block($content_x, $content_y, $width, $h);
- $child->reflow();
-
- if (!$page->in_nested_table()) {
- // Check if a split has occured
- $page->check_page_break($child);
- }
-
- }
-
- // Assign heights to our cells:
- $style->height = $this->_calculate_height();
-
- if ($style->border_collapse === "collapse") {
- // Unset our borders because our cells are now using them
- $style->border_style = "none";
- }
-
- $page->table_reflow_end();
-
- // Debugging:
- //echo ($this->_frame->get_cellmap());
-
- if ($block && $style->float === "none" && $frame->is_in_flow()) {
- $block->add_frame_to_line($frame);
- $block->add_line();
- }
- }
-
- /**
- * @return array|null
- */
- function get_min_max_width()
- {
- if (!is_null($this->_min_max_cache)) {
- return $this->_min_max_cache;
- }
-
- $style = $this->_frame->get_style();
-
- $this->_frame->normalise();
-
- // Add the cells to the cellmap (this will calcluate column widths as
- // frames are added)
- $this->_frame->get_cellmap()->add_frame($this->_frame);
-
- // Find the min/max width of the table and sort the columns into
- // absolute/percent/auto arrays
- $this->_state = array();
- $this->_state["min_width"] = 0;
- $this->_state["max_width"] = 0;
-
- $this->_state["percent_used"] = 0;
- $this->_state["absolute_used"] = 0;
- $this->_state["auto_min"] = 0;
-
- $this->_state["absolute"] = array();
- $this->_state["percent"] = array();
- $this->_state["auto"] = array();
-
- $columns =& $this->_frame->get_cellmap()->get_columns();
- foreach (array_keys($columns) as $i) {
- $this->_state["min_width"] += $columns[$i]["min-width"];
- $this->_state["max_width"] += $columns[$i]["max-width"];
-
- if ($columns[$i]["absolute"] > 0) {
- $this->_state["absolute"][] = $i;
- $this->_state["absolute_used"] += $columns[$i]["absolute"];
- } else if ($columns[$i]["percent"] > 0) {
- $this->_state["percent"][] = $i;
- $this->_state["percent_used"] += $columns[$i]["percent"];
- } else {
- $this->_state["auto"][] = $i;
- $this->_state["auto_min"] += $columns[$i]["min-width"];
- }
- }
-
- // Account for margins & padding
- $dims = array($style->border_left_width,
- $style->border_right_width,
- $style->padding_left,
- $style->padding_right,
- $style->margin_left,
- $style->margin_right);
-
- if ($style->border_collapse !== "collapse") {
- list($dims[]) = $style->border_spacing;
- }
-
- $delta = (float)$style->length_in_pt($dims, $this->_frame->get_containing_block("w"));
-
- $this->_state["min_width"] += $delta;
- $this->_state["max_width"] += $delta;
-
- return $this->_min_max_cache = array(
- $this->_state["min_width"],
- $this->_state["max_width"],
- "min" => $this->_state["min_width"],
- "max" => $this->_state["max_width"],
- );
- }
-}
diff --git a/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php b/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php
deleted file mode 100644
index 13a198745..000000000
--- a/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameReflower;
-
-use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
-use Dompdf\FrameDecorator\Table as TableFrameDecorator;
-
-/**
- * Reflows table row groups (e.g. tbody tags)
- *
- * @package dompdf
- */
-class TableRowGroup extends AbstractFrameReflower
-{
-
- /**
- * TableRowGroup constructor.
- * @param \Dompdf\Frame $frame
- */
- function __construct($frame)
- {
- parent::__construct($frame);
- }
-
- /**
- * @param BlockFrameDecorator|null $block
- */
- function reflow(BlockFrameDecorator $block = null)
- {
- $page = $this->_frame->get_root();
-
- $style = $this->_frame->get_style();
-
- // Our width is equal to the width of our parent table
- $table = TableFrameDecorator::find_parent_table($this->_frame);
-
- $cb = $this->_frame->get_containing_block();
-
- foreach ($this->_frame->get_children() as $child) {
- // Bail if the page is full
- if ($page->is_full()) {
- return;
- }
-
- $child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]);
- $child->reflow();
-
- // Check if a split has occured
- $page->check_page_break($child);
- }
-
- if ($page->is_full()) {
- return;
- }
-
- $cellmap = $table->get_cellmap();
- $style->width = $cellmap->get_frame_width($this->_frame);
- $style->height = $cellmap->get_frame_height($this->_frame);
-
- $this->_frame->set_position($cellmap->get_frame_position($this->_frame));
-
- if ($table->get_style()->border_collapse === "collapse") {
- // Unset our borders because our cells are now using them
- $style->border_style = "none";
- }
- }
-}
diff --git a/library/vendor/dompdf/src/FrameReflower/Text.php b/library/vendor/dompdf/src/FrameReflower/Text.php
deleted file mode 100644
index 321efeba5..000000000
--- a/library/vendor/dompdf/src/FrameReflower/Text.php
+++ /dev/null
@@ -1,511 +0,0 @@
-
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\FrameReflower;
-
-use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
-use Dompdf\FrameDecorator\Text as TextFrameDecorator;
-use Dompdf\FontMetrics;
-use Dompdf\Helpers;
-
-/**
- * Reflows text frames.
- *
- * @package dompdf
- */
-class Text extends AbstractFrameReflower
-{
-
- /**
- * @var BlockFrameDecorator
- */
- protected $_block_parent; // Nearest block-level ancestor
-
- /**
- * @var TextFrameDecorator
- */
- protected $_frame;
-
- public static $_whitespace_pattern = "/[ \t\r\n\f]+/u";
-
- /**
- * @var FontMetrics
- */
- private $fontMetrics;
-
- /**
- * @param TextFrameDecorator $frame
- * @param FontMetrics $fontMetrics
- */
- public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics)
- {
- parent::__construct($frame);
- $this->setFontMetrics($fontMetrics);
- }
-
- /**
- * @param $text
- * @return mixed
- */
- protected function _collapse_white_space($text)
- {
- //$text = $this->_frame->get_text();
-// if ( $this->_block_parent->get_current_line_box->w == 0 )
-// $text = ltrim($text, " \n\r\t");
- return preg_replace(self::$_whitespace_pattern, " ", $text);
- }
-
- /**
- * @param $text
- * @return bool|int
- */
- protected function _line_break($text)
- {
- $style = $this->_frame->get_style();
- $size = $style->font_size;
- $font = $style->font_family;
- $current_line = $this->_block_parent->get_current_line_box();
-
- // Determine the available width
- $line_width = $this->_frame->get_containing_block("w");
- $current_line_width = $current_line->left + $current_line->w + $current_line->right;
-
- $available_width = $line_width - $current_line_width;
-
- // Account for word-spacing
- $word_spacing = (float)$style->length_in_pt($style->word_spacing);
- $char_spacing = (float)$style->length_in_pt($style->letter_spacing);
-
- // Determine the frame width including margin, padding & border
- $text_width = $this->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $char_spacing);
- $mbp_width =
- (float)$style->length_in_pt(array($style->margin_left,
- $style->border_left_width,
- $style->padding_left,
- $style->padding_right,
- $style->border_right_width,
- $style->margin_right), $line_width);
-
- $frame_width = $text_width + $mbp_width;
-
-// Debugging:
-// Helpers::pre_r("Text: '" . htmlspecialchars($text). "'");
-// Helpers::pre_r("width: " .$frame_width);
-// Helpers::pre_r("textwidth + delta: $text_width + $mbp_width");
-// Helpers::pre_r("font-size: $size");
-// Helpers::pre_r("cb[w]: " .$line_width);
-// Helpers::pre_r("available width: " . $available_width);
-// Helpers::pre_r("current line width: " . $current_line_width);
-
-// Helpers::pre_r($words);
-
- if ($frame_width <= $available_width) {
- return false;
- }
-
- // split the text into words
- $words = preg_split('/([\s-]+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
- $wc = count($words);
-
- // Determine the split point
- $width = 0;
- $str = "";
- reset($words);
-
- // @todo support ,
- for ($i = 0; $i < $wc; $i += 2) {
- $word = $words[$i] . (isset($words[$i + 1]) ? $words[$i + 1] : "");
- $word_width = $this->getFontMetrics()->getTextWidth($word, $font, $size, $word_spacing, $char_spacing);
- if ($width + $word_width + $mbp_width > $available_width) {
- break;
- }
-
- $width += $word_width;
- $str .= $word;
- }
-
- $break_word = ($style->word_wrap === "break-word");
-
- // The first word has overflowed. Force it onto the line
- if ($current_line_width == 0 && $width == 0) {
- $s = "";
- $last_width = 0;
-
- if ($break_word) {
- for ($j = 0; $j < strlen($word); $j++) {
- $s .= $word[$j];
- $_width = $this->getFontMetrics()->getTextWidth($s, $font, $size, $word_spacing, $char_spacing);
- if ($_width > $available_width) {
- break;
- }
-
- $last_width = $_width;
- }
- }
-
- if ($break_word && $last_width > 0) {
- //$width += $last_width;
- $str .= substr($s, 0, -1);
- } else {
- //$width += $word_width;
- $str .= $word;
- }
- }
-
- $offset = mb_strlen($str);
-
- // More debugging:
- // var_dump($str);
- // print_r("Width: ". $width);
- // print_r("Offset: " . $offset);
-
- return $offset;
- }
-
- //........................................................................
-
- /**
- * @param $text
- * @return bool|int
- */
- protected function _newline_break($text)
- {
- if (($i = mb_strpos($text, "\n")) === false) {
- return false;
- }
-
- return $i + 1;
- }
-
- /**
- *
- */
- protected function _layout_line()
- {
- $frame = $this->_frame;
- $style = $frame->get_style();
- $text = $frame->get_text();
- $size = $style->font_size;
- $font = $style->font_family;
-
- // Determine the text height
- $style->height = $this->getFontMetrics()->getFontHeight($font, $size);
-
- $split = false;
- $add_line = false;
-
- // Handle text transform:
- // http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
- switch (strtolower($style->text_transform)) {
- default:
- break;
- case "capitalize":
- $text = Helpers::mb_ucwords($text);
- break;
- case "uppercase":
- $text = mb_convert_case($text, MB_CASE_UPPER);
- break;
- case "lowercase":
- $text = mb_convert_case($text, MB_CASE_LOWER);
- break;
- }
-
- // Handle white-space property:
- // http://www.w3.org/TR/CSS21/text.html#propdef-white-space
- switch ($style->white_space) {
- default:
- case "normal":
- $frame->set_text($text = $this->_collapse_white_space($text));
- if ($text == "") {
- break;
- }
-
- $split = $this->_line_break($text);
- break;
-
- case "pre":
- $split = $this->_newline_break($text);
- $add_line = $split !== false;
- break;
-
- case "nowrap":
- $frame->set_text($text = $this->_collapse_white_space($text));
- break;
-
- case "pre-wrap":
- $split = $this->_newline_break($text);
-
- if (($tmp = $this->_line_break($text)) !== false) {
- $add_line = $split < $tmp;
- $split = min($tmp, $split);
- } else
- $add_line = true;
-
- break;
-
- case "pre-line":
- // Collapse white-space except for \n
- $frame->set_text($text = preg_replace("/[ \t]+/u", " ", $text));
-
- if ($text == "") {
- break;
- }
-
- $split = $this->_newline_break($text);
-
- if (($tmp = $this->_line_break($text)) !== false) {
- $add_line = $split < $tmp;
- $split = min($tmp, $split);
- } else {
- $add_line = true;
- }
-
- break;
-
- }
-
- // Handle degenerate case
- if ($text === "") {
- return;
- }
-
- if ($split !== false) {
- // Handle edge cases
- if ($split == 0 && $text === " ") {
- $frame->set_text("");
- return;
- }
-
- if ($split == 0) {
- // Trim newlines from the beginning of the line
- //$this->_frame->set_text(ltrim($text, "\n\r"));
-
- $this->_block_parent->maximize_line_height($style->height, $frame);
- $this->_block_parent->add_line();
- $frame->position();
-
- // Layout the new line
- $this->_layout_line();
- } else if ($split < mb_strlen($frame->get_text())) {
- // split the line if required
- $frame->split_text($split);
-
- $t = $frame->get_text();
-
- // Remove any trailing newlines
- if ($split > 1 && $t[$split - 1] === "\n" && !$frame->is_pre()) {
- $frame->set_text(mb_substr($t, 0, -1));
- }
-
- // Do we need to trim spaces on wrapped lines? This might be desired, however, we
- // can't trim the lines here or the layout will be affected if trimming the line
- // leaves enough space to fit the next word in the text stream (because pdf layout
- // is performed elsewhere).
- /*if (!$this->_frame->get_prev_sibling() && !$this->_frame->get_next_sibling()) {
- $t = $this->_frame->get_text();
- $this->_frame->set_text( trim($t) );
- }*/
- }
-
- if ($add_line) {
- $this->_block_parent->add_line();
- $frame->position();
- }
- } else {
- // Remove empty space from start and end of line, but only where there isn't an inline sibling
- // and the parent node isn't an inline element with siblings
- // FIXME: Include non-breaking spaces?
- $t = $frame->get_text();
- $parent = $frame->get_parent();
- $is_inline_frame = ($parent instanceof \Dompdf\FrameDecorator\Inline);
-
- if ((!$is_inline_frame && !$frame->get_next_sibling()) /* ||
- ( $is_inline_frame && !$parent->get_next_sibling())*/
- ) { // fails BOLD UNDERLINED becomes BOLDUNDERLINED
- $t = rtrim($t);
- }
-
- if ((!$is_inline_frame && !$frame->get_prev_sibling()) /* ||
- ( $is_inline_frame && !$parent->get_prev_sibling())*/
- ) { // AB C fails (the whitespace is removed)
- $t = ltrim($t);
- }
-
- $frame->set_text($t);
- }
-
- // Set our new width
- $width = $frame->recalculate_width();
- }
-
- /**
- * @param BlockFrameDecorator|null $block
- */
- function reflow(BlockFrameDecorator $block = null)
- {
- $frame = $this->_frame;
- $page = $frame->get_root();
- $page->check_forced_page_break($this->_frame);
-
- if ($page->is_full()) {
- return;
- }
-
- $this->_block_parent = /*isset($block) ? $block : */
- $frame->find_block_parent();
-
- // Left trim the text if this is the first text on the line and we're
- // collapsing white space
-// if ( $this->_block_parent->get_current_line()->w == 0 &&
-// ($frame->get_style()->white_space !== "pre" ||
-// $frame->get_style()->white_space !== "pre-wrap") ) {
-// $frame->set_text( ltrim( $frame->get_text() ) );
-// }
-
- $frame->position();
-
- $this->_layout_line();
-
- if ($block) {
- $block->add_frame_to_line($frame);
- }
- }
-
- //........................................................................
-
- // Returns an array(0 => min, 1 => max, "min" => min, "max" => max) of the
- // minimum and maximum widths of this frame
- function get_min_max_width()
- {
- /*if ( !is_null($this->_min_max_cache) )
- return $this->_min_max_cache;*/
- $frame = $this->_frame;
- $style = $frame->get_style();
- $this->_block_parent = $frame->find_block_parent();
- $line_width = $frame->get_containing_block("w");
-
- $str = $text = $frame->get_text();
- $size = $style->font_size;
- $font = $style->font_family;
-
- $word_spacing = (float)$style->length_in_pt($style->word_spacing);
- $char_spacing = (float)$style->length_in_pt($style->letter_spacing);
-
- switch ($style->white_space) {
- default:
- case "normal":
- $str = preg_replace(self::$_whitespace_pattern, " ", $str);
- case "pre-wrap":
- case "pre-line":
-
- // Find the longest word (i.e. minimum length)
-
- // This technique (using arrays & an anonymous function) is actually
- // faster than doing a single-pass character by character scan. Heh,
- // yes I took the time to bench it ;)
- $words = array_flip(preg_split("/[\s-]+/u", $str, -1, PREG_SPLIT_DELIM_CAPTURE));
- $root = $this;
- array_walk($words, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) {
- $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing);
- });
-
- arsort($words);
- $min = reset($words);
- break;
-
- case "pre":
- $lines = array_flip(preg_split("/\n/u", $str));
- $root = $this;
- array_walk($lines, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) {
- $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing);
- });
-
- arsort($lines);
- $min = reset($lines);
- break;
-
- case "nowrap":
- $min = $this->getFontMetrics()->getTextWidth($this->_collapse_white_space($str), $font, $size, $word_spacing, $char_spacing);
- break;
- }
-
- switch ($style->white_space) {
- default:
- case "normal":
- case "nowrap":
- $str = preg_replace(self::$_whitespace_pattern, " ", $text);
- break;
-
- case "pre-line":
- //XXX: Is this correct?
- $str = preg_replace("/[ \t]+/u", " ", $text);
-
- case "pre-wrap":
- // Find the longest word (i.e. minimum length)
- $lines = array_flip(preg_split("/\n/", $text));
- $root = $this;
- array_walk($lines, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) {
- $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing);
- });
- arsort($lines);
- reset($lines);
- $str = key($lines);
- break;
- }
-
- $max = $this->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing);
-
- $delta = (float)$style->length_in_pt(array($style->margin_left,
- $style->border_left_width,
- $style->padding_left,
- $style->padding_right,
- $style->border_right_width,
- $style->margin_right), $line_width);
- $min += $delta;
- $min_word = $min;
- $max += $delta;
-
- if ($style->word_wrap === 'break-word') {
- // If it is allowed to break words, the min width is the widest character.
- // But for performance reasons, we only check the first character.
- $char = mb_substr($str, 0, 1);
- $min_char = $this->getFontMetrics()->getTextWidth($char, $font, $size, $word_spacing, $char_spacing);
- $min = $delta + $min_char;
- }
-
- return $this->_min_max_cache = array($min, $max, $min_word, "min" => $min, "max" => $max, 'min_word' => $min_word);
- }
-
- /**
- * @param FontMetrics $fontMetrics
- * @return $this
- */
- public function setFontMetrics(FontMetrics $fontMetrics)
- {
- $this->fontMetrics = $fontMetrics;
- return $this;
- }
-
- /**
- * @return FontMetrics
- */
- public function getFontMetrics()
- {
- return $this->fontMetrics;
- }
-
- /**
- * Determine current frame width based on contents
- *
- * @return float
- */
- public function calculate_auto_width()
- {
- return $this->_frame->recalculate_width();
- }
-}
diff --git a/library/vendor/dompdf/src/Image/Cache.php b/library/vendor/dompdf/src/Image/Cache.php
deleted file mode 100644
index e1139b11d..000000000
--- a/library/vendor/dompdf/src/Image/Cache.php
+++ /dev/null
@@ -1,186 +0,0 @@
-
- * @author Helmut Tischer
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\Image;
-
-use Dompdf\Dompdf;
-use Dompdf\Helpers;
-use Dompdf\Exception\ImageException;
-
-/**
- * Static class that resolves image urls and downloads and caches
- * remote images if required.
- *
- * @package dompdf
- */
-class Cache
-{
- /**
- * Array of downloaded images. Cached so that identical images are
- * not needlessly downloaded.
- *
- * @var array
- */
- protected static $_cache = array();
-
- /**
- * The url to the "broken image" used when images can't be loaded
- *
- * @var string
- */
- public static $broken_image = "data:image/svg+xml;charset=utf8,%3C?xml version='1.0'?%3E%3Csvg width='64' height='64' xmlns='http://www.w3.org/2000/svg'%3E%3Cg%3E%3Crect stroke='%23666666' id='svg_1' height='60.499994' width='60.166667' y='1.666669' x='1.999998' stroke-width='1.5' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_3' y2='59.333253' x2='59.749916' y1='4.333415' x1='4.250079' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_4' y2='59.999665' x2='4.062838' y1='3.750342' x1='60.062164' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3C/g%3E%3C/svg%3E";
-
- public static $error_message = "Image not found or type unknown";
-
- /**
- * Current dompdf instance
- *
- * @var Dompdf
- */
- protected static $_dompdf;
-
- /**
- * Resolve and fetch an image for use.
- *
- * @param string $url The url of the image
- * @param string $protocol Default protocol if none specified in $url
- * @param string $host Default host if none specified in $url
- * @param string $base_path Default path if none specified in $url
- * @param Dompdf $dompdf The Dompdf instance
- *
- * @throws ImageException
- * @return array An array with two elements: The local path to the image and the image extension
- */
- static function resolve_url($url, $protocol, $host, $base_path, Dompdf $dompdf)
- {
- self::$_dompdf = $dompdf;
-
- $protocol = mb_strtolower($protocol);
- $parsed_url = Helpers::explode_url($url);
- $message = null;
-
- $remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != "");
-
- $data_uri = strpos($parsed_url['protocol'], "data:") === 0;
- $full_url = null;
- $enable_remote = $dompdf->getOptions()->getIsRemoteEnabled();
-
- try {
-
- // Remote not allowed and is not DataURI
- if (!$enable_remote && $remote && !$data_uri) {
- throw new ImageException("Remote file access is disabled.", E_WARNING);
- } // Remote allowed or DataURI
- else {
- if ($enable_remote && $remote || $data_uri) {
- // Download remote files to a temporary directory
- $full_url = Helpers::build_url($protocol, $host, $base_path, $url);
-
- // From cache
- if (isset(self::$_cache[$full_url])) {
- $resolved_url = self::$_cache[$full_url];
- } // From remote
- else {
- $tmp_dir = $dompdf->getOptions()->getTempDir();
- $resolved_url = @tempnam($tmp_dir, "ca_dompdf_img_");
- $image = "";
-
- if ($data_uri) {
- if ($parsed_data_uri = Helpers::parse_data_uri($url)) {
- $image = $parsed_data_uri['data'];
- }
- } else {
- list($image, $http_response_header) = Helpers::getFileContent($full_url, $dompdf->getHttpContext());
- }
-
- // Image not found or invalid
- if (strlen($image) == 0) {
- $msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found");
- throw new ImageException($msg, E_WARNING);
- } // Image found, put in cache and process
- else {
- //e.g. fetch.php?media=url.jpg&cache=1
- //- Image file name might be one of the dynamic parts of the url, don't strip off!
- //- a remote url does not need to have a file extension at all
- //- local cached file does not have a matching file extension
- //Therefore get image type from the content
- file_put_contents($resolved_url, $image);
- }
- }
- } // Not remote, local image
- else {
- $resolved_url = Helpers::build_url($protocol, $host, $base_path, $url);
- }
- }
-
- // Check if the local file is readable
- if (!is_readable($resolved_url) || !filesize($resolved_url)) {
- throw new ImageException("Image not readable or empty", E_WARNING);
- } // Check is the file is an image
- else {
- list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $dompdf->getHttpContext());
-
- // Known image type
- if ($width && $height && in_array($type, array("gif", "png", "jpeg", "bmp", "svg"))) {
- //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup.
- //Only execute on successful caching of remote image.
- if ($enable_remote && $remote || $data_uri) {
- self::$_cache[$full_url] = $resolved_url;
- }
- } // Unknown image type
- else {
- throw new ImageException("Image type unknown", E_WARNING);
- }
- }
- } catch (ImageException $e) {
- $resolved_url = self::$broken_image;
- $type = "png";
- $message = self::$error_message;
- Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine());
- }
-
- return array($resolved_url, $type, $message);
- }
-
- /**
- * Unlink all cached images (i.e. temporary images either downloaded
- * or converted)
- */
- static function clear()
- {
- if (empty(self::$_cache) || self::$_dompdf->getOptions()->getDebugKeepTemp()) {
- return;
- }
-
- foreach (self::$_cache as $file) {
- if (self::$_dompdf->getOptions()->getDebugPng()) {
- print "[clear unlink $file]";
- }
- unlink($file);
- }
-
- self::$_cache = array();
- }
-
- static function detect_type($file, $context = null)
- {
- list(, , $type) = Helpers::dompdf_getimagesize($file, $context);
-
- return $type;
- }
-
- static function is_broken($url)
- {
- return $url === self::$broken_image;
- }
-}
-
-if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.svg"))) {
- Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.svg");
-}
\ No newline at end of file
diff --git a/library/vendor/dompdf/src/Positioner/Absolute.php b/library/vendor/dompdf/src/Positioner/Absolute.php
deleted file mode 100644
index 30dac509a..000000000
--- a/library/vendor/dompdf/src/Positioner/Absolute.php
+++ /dev/null
@@ -1,118 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-namespace Dompdf\Positioner;
-
-use Dompdf\FrameDecorator\AbstractFrameDecorator;
-
-/**
- * Positions absolutely positioned frames
- */
-class Absolute extends AbstractPositioner
-{
-
- /**
- * @param AbstractFrameDecorator $frame
- */
- function position(AbstractFrameDecorator $frame)
- {
- $style = $frame->get_style();
-
- $p = $frame->find_positionned_parent();
-
- list($x, $y, $w, $h) = $frame->get_containing_block();
-
- $top = $style->length_in_pt($style->top, $h);
- $right = $style->length_in_pt($style->right, $w);
- $bottom = $style->length_in_pt($style->bottom, $h);
- $left = $style->length_in_pt($style->left, $w);
-
- if ($p && !($left === "auto" && $right === "auto")) {
- // Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top)
- list($x, $y, $w, $h) = $p->get_padding_box();
- }
-
- list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height());
-
- $orig_style = $frame->get_original_style();
- $orig_width = $orig_style->width;
- $orig_height = $orig_style->height;
-
- /****************************
- *
- * Width auto:
- * ____________| left=auto | left=fixed |
- * right=auto | A | B |
- * right=fixed | C | D |
- *
- * Width fixed:
- * ____________| left=auto | left=fixed |
- * right=auto | E | F |
- * right=fixed | G | H |
- *****************************/
-
- if ($left === "auto") {
- if ($right === "auto") {
- // A or E - Keep the frame at the same position
- $x = $x + $frame->find_block_parent()->get_current_line_box()->w;
- } else {
- if ($orig_width === "auto") {
- // C
- $x += $w - $width - $right;
- } else {
- // G
- $x += $w - $width - $right;
- }
- }
- } else {
- if ($right === "auto") {
- // B or F
- $x += (float)$left;
- } else {
- if ($orig_width === "auto") {
- // D - TODO change width
- $x += (float)$left;
- } else {
- // H - Everything is fixed: left + width win
- $x += (float)$left;
- }
- }
- }
-
- // The same vertically
- if ($top === "auto") {
- if ($bottom === "auto") {
- // A or E - Keep the frame at the same position
- $y = $frame->find_block_parent()->get_current_line_box()->y;
- } else {
- if ($orig_height === "auto") {
- // C
- $y += (float)$h - $height - (float)$bottom;
- } else {
- // G
- $y += (float)$h - $height - (float)$bottom;
- }
- }
- } else {
- if ($bottom === "auto") {
- // B or F
- $y += (float)$top;
- } else {
- if ($orig_height === "auto") {
- // D - TODO change height
- $y += (float)$top;
- } else {
- // H - Everything is fixed: top + height win
- $y += (float)$top;
- }
- }
- }
-
- $frame->set_position($x, $y);
- }
-}
diff --git a/library/vendor/dompdf/src/Positioner/Fixed.php b/library/vendor/dompdf/src/Positioner/Fixed.php
deleted file mode 100644
index 556254b85..000000000
--- a/library/vendor/dompdf/src/Positioner/Fixed.php
+++ /dev/null
@@ -1,89 +0,0 @@
-
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-namespace Dompdf\Positioner;
-
-use Dompdf\FrameDecorator\AbstractFrameDecorator;
-
-/**
- * Positions fixely positioned frames
- */
-class Fixed extends AbstractPositioner
-{
-
- /**
- * @param AbstractFrameDecorator $frame
- */
- function position(AbstractFrameDecorator $frame)
- {
- $style = $frame->get_original_style();
- $root = $frame->get_root();
- $initialcb = $root->get_containing_block();
- $initialcb_style = $root->get_style();
-
- $p = $frame->find_block_parent();
- if ($p) {
- $p->add_line();
- }
-
- // Compute the margins of the @page style
- $margin_top = (float)$initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]);
- $margin_right = (float)$initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]);
- $margin_bottom = (float)$initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]);
- $margin_left = (float)$initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]);
-
- // The needed computed style of the element
- $height = (float)$style->length_in_pt($style->height, $initialcb["h"]);
- $width = (float)$style->length_in_pt($style->width, $initialcb["w"]);
-
- $top = $style->length_in_pt($style->top, $initialcb["h"]);
- $right = $style->length_in_pt($style->right, $initialcb["w"]);
- $bottom = $style->length_in_pt($style->bottom, $initialcb["h"]);
- $left = $style->length_in_pt($style->left, $initialcb["w"]);
-
- $y = $margin_top;
- if (isset($top)) {
- $y = (float)$top + $margin_top;
- if ($top === "auto") {
- $y = $margin_top;
- if (isset($bottom) && $bottom !== "auto") {
- $y = $initialcb["h"] - $bottom - $margin_bottom;
- if ($frame->is_auto_height()) {
- $y -= $height;
- } else {
- $y -= $frame->get_margin_height();
- }
- }
- }
- }
-
- $x = $margin_left;
- if (isset($left)) {
- $x = (float)$left + $margin_left;
- if ($left === "auto") {
- $x = $margin_left;
- if (isset($right) && $right !== "auto") {
- $x = $initialcb["w"] - $right - $margin_right;
- if ($frame->is_auto_width()) {
- $x -= $width;
- } else {
- $x -= $frame->get_margin_width();
- }
- }
- }
- }
-
- $frame->set_position($x, $y);
-
- $children = $frame->get_children();
- foreach ($children as $child) {
- $child->set_position($x, $y);
- }
- }
-}
\ No newline at end of file
diff --git a/library/vendor/dompdf/src/Positioner/Inline.php b/library/vendor/dompdf/src/Positioner/Inline.php
deleted file mode 100644
index bcea2ba04..000000000
--- a/library/vendor/dompdf/src/Positioner/Inline.php
+++ /dev/null
@@ -1,77 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-namespace Dompdf\Positioner;
-
-use Dompdf\FrameDecorator\AbstractFrameDecorator;
-use Dompdf\FrameDecorator\Inline as InlineFrameDecorator;
-use Dompdf\Exception;
-
-/**
- * Positions inline frames
- *
- * @package dompdf
- */
-class Inline extends AbstractPositioner
-{
-
- /**
- * @param AbstractFrameDecorator $frame
- * @throws Exception
- */
- function position(AbstractFrameDecorator $frame)
- {
- /**
- * Find our nearest block level parent and access its lines property.
- * @var BlockFrameDecorator
- */
- $p = $frame->find_block_parent();
-
- // Debugging code:
-
- // Helpers::pre_r("\nPositioning:");
- // Helpers::pre_r("Me: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
- // Helpers::pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")");
-
- // End debugging
-
- if (!$p) {
- throw new Exception("No block-level parent found. Not good.");
- }
-
- $f = $frame;
-
- $cb = $f->get_containing_block();
- $line = $p->get_current_line_box();
-
- // Skip the page break if in a fixed position element
- $is_fixed = false;
- while ($f = $f->get_parent()) {
- if ($f->get_style()->position === "fixed") {
- $is_fixed = true;
- break;
- }
- }
-
- $f = $frame;
-
- if (!$is_fixed && $f->get_parent() &&
- $f->get_parent() instanceof InlineFrameDecorator &&
- $f->is_text_node()
- ) {
- $min_max = $f->get_reflower()->get_min_max_width();
-
- // If the frame doesn't fit in the current line, a line break occurs
- if ($min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right)) {
- $p->add_line();
- }
- }
-
- $f->set_position($cb["x"] + $line->w, $line->y);
- }
-}
diff --git a/library/vendor/dompdf/src/Positioner/ListBullet.php b/library/vendor/dompdf/src/Positioner/ListBullet.php
deleted file mode 100644
index 36691f232..000000000
--- a/library/vendor/dompdf/src/Positioner/ListBullet.php
+++ /dev/null
@@ -1,76 +0,0 @@
-
- * @author Helmut Tischer
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-namespace Dompdf\Positioner;
-
-use Dompdf\FrameDecorator\AbstractFrameDecorator;
-
-/**
- * Positions list bullets
- *
- * @package dompdf
- */
-class ListBullet extends AbstractPositioner
-{
-
- /**
- * @param AbstractFrameDecorator $frame
- */
- function position(AbstractFrameDecorator $frame)
- {
-
- // Bullets & friends are positioned an absolute distance to the left of
- // the content edge of their parent element
- $cb = $frame->get_containing_block();
-
- // Note: this differs from most frames in that we must position
- // ourselves after determining our width
- $x = $cb["x"] - $frame->get_width();
-
- $p = $frame->find_block_parent();
-
- $y = $p->get_current_line_box()->y;
-
- // This is a bit of a hack...
- $n = $frame->get_next_sibling();
- if ($n) {
- $style = $n->get_style();
- $line_height = $style->length_in_pt($style->line_height, $style->font_size);
- $offset = (float)$style->length_in_pt($line_height, $n->get_containing_block("h")) - $frame->get_height();
- $y += $offset / 2;
- }
-
- // Now the position is the left top of the block which should be marked with the bullet.
- // We tried to find out the y of the start of the first text character within the block.
- // But the top margin/padding does not fit, neither from this nor from the next sibling
- // The "bit of a hack" above does not work also.
-
- // Instead let's position the bullet vertically centered to the block which should be marked.
- // But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent()
- // the get_containing_block is paper width and the entire list as height.
-
- // if ($p) {
- // //$cb = $n->get_containing_block();
- // $cb = $p->get_containing_block();
- // $y += $cb["h"]/2;
- // print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':';
- // }
-
- // Todo:
- // For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing
-
- /*$style = $p->get_style();
- $font_size = $style->font_size;
- $line_height = (float)$style->length_in_pt($style->line_height, $font_size);
- $y += ($line_height - $font_size) / 2; */
-
- //Position is x-end y-top of character position of the bullet.
- $frame->set_position($x, $y);
- }
-}
diff --git a/library/vendor/dompdf/src/Renderer/AbstractRenderer.php b/library/vendor/dompdf/src/Renderer/AbstractRenderer.php
deleted file mode 100644
index 502e78a02..000000000
--- a/library/vendor/dompdf/src/Renderer/AbstractRenderer.php
+++ /dev/null
@@ -1,923 +0,0 @@
-
- * @author Helmut Tischer
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\Renderer;
-
-use Dompdf\Adapter\CPDF;
-use Dompdf\Css\Color;
-use Dompdf\Css\Style;
-use Dompdf\Dompdf;
-use Dompdf\Helpers;
-use Dompdf\Frame;
-use Dompdf\Image\Cache;
-
-/**
- * Base renderer class
- *
- * @package dompdf
- */
-abstract class AbstractRenderer
-{
-
- /**
- * Rendering backend
- *
- * @var \Dompdf\Canvas
- */
- protected $_canvas;
-
- /**
- * Current dompdf instance
- *
- * @var Dompdf
- */
- protected $_dompdf;
-
- /**
- * Class constructor
- *
- * @param Dompdf $dompdf The current dompdf instance
- */
- function __construct(Dompdf $dompdf)
- {
- $this->_dompdf = $dompdf;
- $this->_canvas = $dompdf->getCanvas();
- }
-
- /**
- * Render a frame.
- *
- * Specialized in child classes
- *
- * @param Frame $frame The frame to render
- */
- abstract function render(Frame $frame);
-
- /**
- * Render a background image over a rectangular area
- *
- * @param string $url The background image to load
- * @param float $x The left edge of the rectangular area
- * @param float $y The top edge of the rectangular area
- * @param float $width The width of the rectangular area
- * @param float $height The height of the rectangular area
- * @param Style $style The associated Style object
- *
- * @throws \Exception
- */
- protected function _background_image($url, $x, $y, $width, $height, $style)
- {
- if (!function_exists("imagecreatetruecolor")) {
- throw new \Exception("The PHP GD extension is required, but is not installed.");
- }
-
- $sheet = $style->get_stylesheet();
-
- // Skip degenerate cases
- if ($width == 0 || $height == 0) {
- return;
- }
-
- $box_width = $width;
- $box_height = $height;
-
- //debugpng
- if ($this->_dompdf->getOptions()->getDebugPng()) {
- print '[_background_image ' . $url . ']';
- }
-
- list($img, $type, /*$msg*/) = Cache::resolve_url(
- $url,
- $sheet->get_protocol(),
- $sheet->get_host(),
- $sheet->get_base_path(),
- $this->_dompdf
- );
-
- // Bail if the image is no good
- if (Cache::is_broken($img)) {
- return;
- }
-
- //Try to optimize away reading and composing of same background multiple times
- //Postponing read with imagecreatefrom ...()
- //final composition parameters and name not known yet
- //Therefore read dimension directly from file, instead of creating gd object first.
- //$img_w = imagesx($src); $img_h = imagesy($src);
-
- list($img_w, $img_h) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext());
- if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) {
- return;
- }
-
- $repeat = $style->background_repeat;
- $dpi = $this->_dompdf->getOptions()->getDpi();
-
- //Increase background resolution and dependent box size according to image resolution to be placed in
- //Then image can be copied in without resize
- $bg_width = round((float)($width * $dpi) / 72);
- $bg_height = round((float)($height * $dpi) / 72);
-
- //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel
-
- list($bg_x, $bg_y) = $style->background_position;
-
- if (Helpers::is_percent($bg_x)) {
- // The point $bg_x % from the left edge of the image is placed
- // $bg_x % from the left edge of the background rectangle
- $p = ((float)$bg_x) / 100.0;
- $x1 = $p * $img_w;
- $x2 = $p * $bg_width;
-
- $bg_x = $x2 - $x1;
- } else {
- $bg_x = (float)($style->length_in_pt($bg_x) * $dpi) / 72;
- }
-
- $bg_x = round($bg_x + (float)$style->length_in_pt($style->border_left_width) * $dpi / 72);
-
- if (Helpers::is_percent($bg_y)) {
- // The point $bg_y % from the left edge of the image is placed
- // $bg_y % from the left edge of the background rectangle
- $p = ((float)$bg_y) / 100.0;
- $y1 = $p * $img_h;
- $y2 = $p * $bg_height;
-
- $bg_y = $y2 - $y1;
- } else {
- $bg_y = (float)($style->length_in_pt($bg_y) * $dpi) / 72;
- }
-
- $bg_y = round($bg_y + (float)$style->length_in_pt($style->border_top_width) * $dpi / 72);
-
- //clip background to the image area on partial repeat. Nothing to do if img off area
- //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area
- //On no repeat with positive offset: move size/start to have offset==0
- //Handle x/y Dimensions separately
-
- if ($repeat !== "repeat" && $repeat !== "repeat-x") {
- //No repeat x
- if ($bg_x < 0) {
- $bg_width = $img_w + $bg_x;
- } else {
- $x += ($bg_x * 72) / $dpi;
- $bg_width = $bg_width - $bg_x;
- if ($bg_width > $img_w) {
- $bg_width = $img_w;
- }
- $bg_x = 0;
- }
-
- if ($bg_width <= 0) {
- return;
- }
-
- $width = (float)($bg_width * 72) / $dpi;
- } else {
- //repeat x
- if ($bg_x < 0) {
- $bg_x = -((-$bg_x) % $img_w);
- } else {
- $bg_x = $bg_x % $img_w;
- if ($bg_x > 0) {
- $bg_x -= $img_w;
- }
- }
- }
-
- if ($repeat !== "repeat" && $repeat !== "repeat-y") {
- //no repeat y
- if ($bg_y < 0) {
- $bg_height = $img_h + $bg_y;
- } else {
- $y += ($bg_y * 72) / $dpi;
- $bg_height = $bg_height - $bg_y;
- if ($bg_height > $img_h) {
- $bg_height = $img_h;
- }
- $bg_y = 0;
- }
- if ($bg_height <= 0) {
- return;
- }
- $height = (float)($bg_height * 72) / $dpi;
- } else {
- //repeat y
- if ($bg_y < 0) {
- $bg_y = -((-$bg_y) % $img_h);
- } else {
- $bg_y = $bg_y % $img_h;
- if ($bg_y > 0) {
- $bg_y -= $img_h;
- }
- }
- }
-
- //Optimization, if repeat has no effect
- if ($repeat === "repeat" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) {
- $repeat = "repeat-x";
- }
-
- if ($repeat === "repeat" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) {
- $repeat = "repeat-y";
- }
-
- if (($repeat === "repeat-x" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) ||
- ($repeat === "repeat-y" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height)
- ) {
- $repeat = "no-repeat";
- }
-
- //Use filename as indicator only
- //different names for different variants to have different copies in the pdf
- //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color)
- //Note: Here, bg_* are the start values, not end values after going through the tile loops!
-
- $filedummy = $img;
-
- $is_png = false;
- $filedummy .= '_' . $bg_width . '_' . $bg_height . '_' . $bg_x . '_' . $bg_y . '_' . $repeat;
-
- //Optimization to avoid multiple times rendering the same image.
- //If check functions are existing and identical image already cached,
- //then skip creation of duplicate, because it is not needed by addImagePng
- if ($this->_canvas instanceof CPDF && $this->_canvas->get_cpdf()->image_iscached($filedummy)) {
- $bg = null;
- } else {
- // Create a new image to fit over the background rectangle
- $bg = imagecreatetruecolor($bg_width, $bg_height);
-
- switch (strtolower($type)) {
- case "png":
- $is_png = true;
- imagesavealpha($bg, true);
- imagealphablending($bg, false);
- $src = imagecreatefrompng($img);
- break;
-
- case "jpeg":
- $src = imagecreatefromjpeg($img);
- break;
-
- case "gif":
- $src = imagecreatefromgif($img);
- break;
-
- case "bmp":
- $src = Helpers::imagecreatefrombmp($img);
- break;
-
- default:
- return; // Unsupported image type
- }
-
- if ($src == null) {
- return;
- }
-
- //Background color if box is not relevant here
- //Non transparent image: box clipped to real size. Background non relevant.
- //Transparent image: The image controls the transparency and lets shine through whatever background.
- //However on transparent image preset the composed image with the transparency color,
- //to keep the transparency when copying over the non transparent parts of the tiles.
- $ti = imagecolortransparent($src);
-
- if ($ti >= 0) {
- $tc = imagecolorsforindex($src, $ti);
- $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']);
- imagefill($bg, 0, 0, $ti);
- imagecolortransparent($bg, $ti);
- }
-
- //This has only an effect for the non repeatable dimension.
- //compute start of src and dest coordinates of the single copy
- if ($bg_x < 0) {
- $dst_x = 0;
- $src_x = -$bg_x;
- } else {
- $src_x = 0;
- $dst_x = $bg_x;
- }
-
- if ($bg_y < 0) {
- $dst_y = 0;
- $src_y = -$bg_y;
- } else {
- $src_y = 0;
- $dst_y = $bg_y;
- }
-
- //For historical reasons exchange meanings of variables:
- //start_* will be the start values, while bg_* will be the temporary start values in the loops
- $start_x = $bg_x;
- $start_y = $bg_y;
-
- // Copy regions from the source image to the background
- if ($repeat === "no-repeat") {
- // Simply place the image on the background
- imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h);
-
- } else if ($repeat === "repeat-x") {
- for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) {
- if ($bg_x < 0) {
- $dst_x = 0;
- $src_x = -$bg_x;
- $w = $img_w + $bg_x;
- } else {
- $dst_x = $bg_x;
- $src_x = 0;
- $w = $img_w;
- }
- imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h);
- }
- } else if ($repeat === "repeat-y") {
-
- for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) {
- if ($bg_y < 0) {
- $dst_y = 0;
- $src_y = -$bg_y;
- $h = $img_h + $bg_y;
- } else {
- $dst_y = $bg_y;
- $src_y = 0;
- $h = $img_h;
- }
- imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h);
- }
- } else if ($repeat === "repeat") {
- for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) {
- for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) {
- if ($bg_x < 0) {
- $dst_x = 0;
- $src_x = -$bg_x;
- $w = $img_w + $bg_x;
- } else {
- $dst_x = $bg_x;
- $src_x = 0;
- $w = $img_w;
- }
-
- if ($bg_y < 0) {
- $dst_y = 0;
- $src_y = -$bg_y;
- $h = $img_h + $bg_y;
- } else {
- $dst_y = $bg_y;
- $src_y = 0;
- $h = $img_h;
- }
- imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h);
- }
- }
- } else {
- print 'Unknown repeat!';
- }
-
- imagedestroy($src);
-
- } /* End optimize away creation of duplicates */
-
- $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height);
-
- //img: image url string
- //img_w, img_h: original image size in px
- //width, height: box size in pt
- //bg_width, bg_height: box size in px
- //x, y: left/top edge of box on page in pt
- //start_x, start_y: placement of image relative to pattern
- //$repeat: repeat mode
- //$bg: GD object of result image
- //$src: GD object of original image
- //When using cpdf and optimization to direct png creation from gd object is available,
- //don't create temp file, but place gd object directly into the pdf
- if (!$is_png && $this->_canvas instanceof CPDF) {
- // Note: CPDF_Adapter image converts y position
- $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg);
- } else {
- $tmp_dir = $this->_dompdf->getOptions()->getTempDir();
- $tmp_name = @tempnam($tmp_dir, "bg_dompdf_img_");
- @unlink($tmp_name);
- $tmp_file = "$tmp_name.png";
-
- //debugpng
- if ($this->_dompdf->getOptions()->getDebugPng()) {
- print '[_background_image ' . $tmp_file . ']';
- }
-
- imagepng($bg, $tmp_file);
- $this->_canvas->image($tmp_file, $x, $y, $width, $height);
- imagedestroy($bg);
-
- //debugpng
- if ($this->_dompdf->getOptions()->getDebugPng()) {
- print '[_background_image unlink ' . $tmp_file . ']';
- }
-
- if (!$this->_dompdf->getOptions()->getDebugKeepTemp()) {
- unlink($tmp_file);
- }
- }
-
- $this->_canvas->clipping_end();
- }
-
- /**
- * @param $style
- * @param $width
- * @return array
- */
- protected function _get_dash_pattern($style, $width)
- {
- $pattern = array();
-
- switch ($style) {
- default:
- /*case "solid":
- case "double":
- case "groove":
- case "inset":
- case "outset":
- case "ridge":*/
- case "none":
- break;
-
- case "dotted":
- if ($width <= 1) {
- $pattern = array($width, $width * 2);
- } else {
- $pattern = array($width);
- }
- break;
-
- case "dashed":
- $pattern = array(3 * $width);
- break;
- }
-
- return $pattern;
- }
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- return;
- }
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- return;
- }
-
- // Border rendering functions
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2);
- }
-
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2);
- }
-
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't.
- if ($corner_style !== "bevel" || $r1 > 0 || $r2 > 0) {
- // do it the simple way
- $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2);
- return;
- }
-
- list($top, $right, $bottom, $left) = $widths;
-
- // All this polygon business is for beveled corners...
- switch ($side) {
- case "top":
- $points = array($x, $y,
- $x + $length, $y,
- $x + $length - $right, $y + $top,
- $x + $left, $y + $top);
- $this->_canvas->polygon($points, $color, null, null, true);
- break;
-
- case "bottom":
- $points = array($x, $y,
- $x + $length, $y,
- $x + $length - $right, $y - $bottom,
- $x + $left, $y - $bottom);
- $this->_canvas->polygon($points, $color, null, null, true);
- break;
-
- case "left":
- $points = array($x, $y,
- $x, $y + $length,
- $x + $left, $y + $length - $bottom,
- $x + $left, $y + $top);
- $this->_canvas->polygon($points, $color, null, null, true);
- break;
-
- case "right":
- $points = array($x, $y,
- $x, $y + $length,
- $x - $right, $y + $length - $bottom,
- $x - $right, $y + $top);
- $this->_canvas->polygon($points, $color, null, null, true);
- break;
-
- default:
- return;
- }
- }
-
- /**
- * @param $side
- * @param $ratio
- * @param $top
- * @param $right
- * @param $bottom
- * @param $left
- * @param $x
- * @param $y
- * @param $length
- * @param $r1
- * @param $r2
- */
- protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2)
- {
- switch ($side) {
- case "top":
- $r1 -= $left * $ratio;
- $r2 -= $right * $ratio;
- $x += $left * $ratio;
- $y += $top * $ratio;
- $length -= $left * $ratio + $right * $ratio;
- break;
-
- case "bottom":
- $r1 -= $right * $ratio;
- $r2 -= $left * $ratio;
- $x += $left * $ratio;
- $y -= $bottom * $ratio;
- $length -= $left * $ratio + $right * $ratio;
- break;
-
- case "left":
- $r1 -= $top * $ratio;
- $r2 -= $bottom * $ratio;
- $x += $left * $ratio;
- $y += $top * $ratio;
- $length -= $top * $ratio + $bottom * $ratio;
- break;
-
- case "right":
- $r1 -= $bottom * $ratio;
- $r2 -= $top * $ratio;
- $x -= $right * $ratio;
- $y += $top * $ratio;
- $length -= $top * $ratio + $bottom * $ratio;
- break;
-
- default:
- return;
- }
- }
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- list($top, $right, $bottom, $left) = $widths;
-
- $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3);
-
- // draw the outer border
- $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
-
- $this->_apply_ratio($side, 2 / 3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
-
- $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
- }
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- list($top, $right, $bottom, $left) = $widths;
-
- $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
-
- $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
-
- $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
-
- $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
-
- }
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- list($top, $right, $bottom, $left) = $widths;
-
- $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
-
- $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
-
- $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
-
- $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
-
- }
-
- /**
- * @param $c
- * @return mixed
- */
- protected function _tint($c)
- {
- if (!is_numeric($c)) {
- return $c;
- }
-
- return min(1, $c + 0.16);
- }
-
- /**
- * @param $c
- * @return mixed
- */
- protected function _shade($c)
- {
- if (!is_numeric($c)) {
- return $c;
- }
-
- return max(0, $c - 0.33);
- }
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- switch ($side) {
- case "top":
- case "left":
- $shade = array_map(array($this, "_shade"), $color);
- $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
- break;
-
- case "bottom":
- case "right":
- $tint = array_map(array($this, "_tint"), $color);
- $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
- break;
-
- default:
- return;
- }
- }
-
- /**
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param int $r1
- * @param int $r2
- */
- protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
- {
- switch ($side) {
- case "top":
- case "left":
- $tint = array_map(array($this, "_tint"), $color);
- $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
- break;
-
- case "bottom":
- case "right":
- $shade = array_map(array($this, "_shade"), $color);
- $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
- break;
-
- default:
- return;
- }
- }
-
- /**
- * Draws a solid, dotted, or dashed line, observing the border radius
- *
- * @param $x
- * @param $y
- * @param $length
- * @param $color
- * @param $widths
- * @param $side
- * @param string $corner_style
- * @param $pattern_name
- * @param int $r1
- * @param int $r2
- *
- * @var $top
- */
- protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0)
- {
- /** used by $$side */
- list($top, $right, $bottom, $left) = $widths;
- $width = $$side;
-
- $pattern = $this->_get_dash_pattern($pattern_name, $width);
-
- $half_width = $width / 2;
- $r1 -= $half_width;
- $r2 -= $half_width;
- $adjust = $r1 / 80;
- $length -= $width;
-
- switch ($side) {
- case "top":
- $x += $half_width;
- $y += $half_width;
-
- if ($r1 > 0) {
- $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90 - $adjust, 135 + $adjust, $color, $width, $pattern);
- }
-
- $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
-
- if ($r2 > 0) {
- $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45 - $adjust, 90 + $adjust, $color, $width, $pattern);
- }
- break;
-
- case "bottom":
- $x += $half_width;
- $y -= $half_width;
-
- if ($r1 > 0) {
- $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225 - $adjust, 270 + $adjust, $color, $width, $pattern);
- }
-
- $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
-
- if ($r2 > 0) {
- $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270 - $adjust, 315 + $adjust, $color, $width, $pattern);
- }
- break;
-
- case "left":
- $y += $half_width;
- $x += $half_width;
-
- if ($r1 > 0) {
- $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135 - $adjust, 180 + $adjust, $color, $width, $pattern);
- }
-
- $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
-
- if ($r2 > 0) {
- $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180 - $adjust, 225 + $adjust, $color, $width, $pattern);
- }
- break;
-
- case "right":
- $y += $half_width;
- $x -= $half_width;
-
- if ($r1 > 0) {
- $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0 - $adjust, 45 + $adjust, $color, $width, $pattern);
- }
-
- $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
-
- if ($r2 > 0) {
- $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315 - $adjust, 360 + $adjust, $color, $width, $pattern);
- }
- break;
- }
- }
-
- /**
- * @param $opacity
- */
- protected function _set_opacity($opacity)
- {
- if (is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0) {
- $this->_canvas->set_opacity($opacity);
- }
- }
-
- /**
- * @param $box
- * @param string $color
- * @param array $style
- */
- protected function _debug_layout($box, $color = "red", $style = array())
- {
- $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], Color::parse($color), 0.1, $style);
- }
-}
diff --git a/library/vendor/dompdf/src/Renderer/Block.php b/library/vendor/dompdf/src/Renderer/Block.php
deleted file mode 100644
index ee0705d8f..000000000
--- a/library/vendor/dompdf/src/Renderer/Block.php
+++ /dev/null
@@ -1,262 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\Renderer;
-
-use Dompdf\Frame;
-use Dompdf\FrameDecorator\AbstractFrameDecorator;
-use Dompdf\Helpers;
-
-/**
- * Renders block frames
- *
- * @package dompdf
- */
-class Block extends AbstractRenderer
-{
-
- /**
- * @param Frame $frame
- */
- function render(Frame $frame)
- {
- $style = $frame->get_style();
- $node = $frame->get_node();
-
- list($x, $y, $w, $h) = $frame->get_border_box();
-
- $this->_set_opacity($frame->get_opacity($style->opacity));
-
- if ($node->nodeName === "body") {
- $h = $frame->get_containing_block("h") - (float)$style->length_in_pt(array(
- $style->margin_top,
- $style->border_top_width,
- $style->border_bottom_width,
- $style->margin_bottom),
- (float)$style->length_in_pt($style->width));
- }
-
- // Handle anchors & links
- if ($node->nodeName === "a" && $href = $node->getAttribute("href")) {
- $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href);
- $this->_canvas->add_link($href, $x, $y, (float)$w, (float)$h);
- }
-
- // Draw our background, border and content
- list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
-
- if ($tl + $tr + $br + $bl > 0) {
- $this->_canvas->clipping_roundrectangle($x, $y, (float)$w, (float)$h, $tl, $tr, $br, $bl);
- }
-
- if (($bg = $style->background_color) !== "transparent") {
- $this->_canvas->filled_rectangle($x, $y, (float)$w, (float)$h, $bg);
- }
-
- if (($url = $style->background_image) && $url !== "none") {
- $this->_background_image($url, $x, $y, $w, $h, $style);
- }
-
- if ($tl + $tr + $br + $bl > 0) {
- $this->_canvas->clipping_end();
- }
-
- $border_box = array($x, $y, $w, $h);
- $this->_render_border($frame, $border_box);
- $this->_render_outline($frame, $border_box);
-
- if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutBlocks()) {
- $this->_debug_layout($frame->get_border_box(), "red");
- if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) {
- $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
- }
- }
-
- if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines() && $frame->get_decorator()) {
- foreach ($frame->get_decorator()->get_line_boxes() as $line) {
- $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
- }
- }
-
- $id = $frame->get_node()->getAttribute("id");
- if (strlen($id) > 0) {
- $this->_canvas->add_named_dest($id);
- }
- }
-
- /**
- * @param AbstractFrameDecorator $frame
- * @param null $border_box
- * @param string $corner_style
- */
- protected function _render_border(AbstractFrameDecorator $frame, $border_box = null, $corner_style = "bevel")
- {
- $style = $frame->get_style();
- $bp = $style->get_border_properties();
-
- if (empty($border_box)) {
- $border_box = $frame->get_border_box();
- }
-
- // find the radius
- $radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h
-
- // Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle
- if (
- in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) &&
- $bp["top"] == $bp["right"] &&
- $bp["right"] == $bp["bottom"] &&
- $bp["bottom"] == $bp["left"] &&
- array_sum($radius) == 0
- ) {
- $props = $bp["top"];
- if ($props["color"] === "transparent" || $props["width"] <= 0) {
- return;
- }
-
- list($x, $y, $w, $h) = $border_box;
- $width = (float)$style->length_in_pt($props["width"]);
- $pattern = $this->_get_dash_pattern($props["style"], $width);
- $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, (float)$w - $width, (float)$h - $width, $props["color"], $width, $pattern);
- return;
- }
-
- // Do it the long way
- $widths = array(
- (float)$style->length_in_pt($bp["top"]["width"]),
- (float)$style->length_in_pt($bp["right"]["width"]),
- (float)$style->length_in_pt($bp["bottom"]["width"]),
- (float)$style->length_in_pt($bp["left"]["width"])
- );
-
- foreach ($bp as $side => $props) {
- list($x, $y, $w, $h) = $border_box;
- $length = 0;
- $r1 = 0;
- $r2 = 0;
-
- if (!$props["style"] ||
- $props["style"] === "none" ||
- $props["width"] <= 0 ||
- $props["color"] == "transparent"
- ) {
- continue;
- }
-
- switch ($side) {
- case "top":
- $length = (float)$w;
- $r1 = $radius["top-left"];
- $r2 = $radius["top-right"];
- break;
-
- case "bottom":
- $length = (float)$w;
- $y += (float)$h;
- $r1 = $radius["bottom-left"];
- $r2 = $radius["bottom-right"];
- break;
-
- case "left":
- $length = (float)$h;
- $r1 = $radius["top-left"];
- $r2 = $radius["bottom-left"];
- break;
-
- case "right":
- $length = (float)$h;
- $x += (float)$w;
- $r1 = $radius["top-right"];
- $r2 = $radius["bottom-right"];
- break;
- default:
- break;
- }
- $method = "_border_" . $props["style"];
-
- // draw rounded corners
- $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2);
- }
- }
-
- /**
- * @param AbstractFrameDecorator $frame
- * @param null $border_box
- * @param string $corner_style
- */
- protected function _render_outline(AbstractFrameDecorator $frame, $border_box = null, $corner_style = "bevel")
- {
- $style = $frame->get_style();
-
- $props = array(
- "width" => $style->outline_width,
- "style" => $style->outline_style,
- "color" => $style->outline_color,
- );
-
- if (!$props["style"] || $props["style"] === "none" || $props["width"] <= 0) {
- return;
- }
-
- if (empty($border_box)) {
- $border_box = $frame->get_border_box();
- }
-
- $offset = (float)$style->length_in_pt($props["width"]);
- $pattern = $this->_get_dash_pattern($props["style"], $offset);
-
- // If the outline style is "solid" we'd better draw a rectangle
- if (in_array($props["style"], array("solid", "dashed", "dotted"))) {
- $border_box[0] -= $offset / 2;
- $border_box[1] -= $offset / 2;
- $border_box[2] += $offset;
- $border_box[3] += $offset;
-
- list($x, $y, $w, $h) = $border_box;
- $this->_canvas->rectangle($x, $y, (float)$w, (float)$h, $props["color"], $offset, $pattern);
- return;
- }
-
- $border_box[0] -= $offset;
- $border_box[1] -= $offset;
- $border_box[2] += $offset * 2;
- $border_box[3] += $offset * 2;
-
- $method = "_border_" . $props["style"];
- $widths = array_fill(0, 4, (float)$style->length_in_pt($props["width"]));
- $sides = array("top", "right", "left", "bottom");
- $length = 0;
-
- foreach ($sides as $side) {
- list($x, $y, $w, $h) = $border_box;
-
- switch ($side) {
- case "top":
- $length = (float)$w;
- break;
-
- case "bottom":
- $length = (float)$w;
- $y += (float)$h;
- break;
-
- case "left":
- $length = (float)$h;
- break;
-
- case "right":
- $length = (float)$h;
- $x += (float)$w;
- break;
- default:
- break;
- }
-
- $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style);
- }
- }
-}
diff --git a/library/vendor/dompdf/src/Renderer/Image.php b/library/vendor/dompdf/src/Renderer/Image.php
deleted file mode 100644
index 87333af71..000000000
--- a/library/vendor/dompdf/src/Renderer/Image.php
+++ /dev/null
@@ -1,139 +0,0 @@
-
- * @author Fabien Ménager
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\Renderer;
-
-use Dompdf\Frame;
-use Dompdf\Image\Cache;
-
-/**
- * Image renderer
- *
- * @access private
- * @package dompdf
- */
-class Image extends Block
-{
-
- /**
- * @param Frame $frame
- */
- function render(Frame $frame)
- {
- // Render background & borders
- $style = $frame->get_style();
- $cb = $frame->get_containing_block();
- list($x, $y, $w, $h) = $frame->get_border_box();
-
- $this->_set_opacity($frame->get_opacity($style->opacity));
-
- list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
-
- $has_border_radius = $tl + $tr + $br + $bl > 0;
-
- if ($has_border_radius) {
- $this->_canvas->clipping_roundrectangle($x, $y, (float)$w, (float)$h, $tl, $tr, $br, $bl);
- }
-
- if (($bg = $style->background_color) !== "transparent") {
- $this->_canvas->filled_rectangle($x, $y, (float)$w, (float)$h, $bg);
- }
-
- if (($url = $style->background_image) && $url !== "none") {
- $this->_background_image($url, $x, $y, $w, $h, $style);
- }
-
- if ($has_border_radius) {
- $this->_canvas->clipping_end();
- }
-
- $this->_render_border($frame);
- $this->_render_outline($frame);
-
- list($x, $y) = $frame->get_padding_box();
-
- $x += (float)$style->length_in_pt($style->padding_left, $cb["w"]);
- $y += (float)$style->length_in_pt($style->padding_top, $cb["h"]);
-
- $w = (float)$style->length_in_pt($style->width, $cb["w"]);
- $h = (float)$style->length_in_pt($style->height, $cb["h"]);
-
- if ($has_border_radius) {
- list($wt, $wr, $wb, $wl) = array(
- $style->border_top_width,
- $style->border_right_width,
- $style->border_bottom_width,
- $style->border_left_width,
- );
-
- // we have to get the "inner" radius
- if ($tl > 0) {
- $tl -= ($wt + $wl) / 2;
- }
- if ($tr > 0) {
- $tr -= ($wt + $wr) / 2;
- }
- if ($br > 0) {
- $br -= ($wb + $wr) / 2;
- }
- if ($bl > 0) {
- $bl -= ($wb + $wl) / 2;
- }
-
- $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
- }
-
- $src = $frame->get_image_url();
- $alt = null;
-
- if (Cache::is_broken($src) &&
- $alt = $frame->get_node()->getAttribute("alt")
- ) {
- $font = $style->font_family;
- $size = $style->font_size;
- $spacing = $style->word_spacing;
- $this->_canvas->text(
- $x,
- $y,
- $alt,
- $font,
- $size,
- $style->color,
- $spacing
- );
- } else {
- $this->_canvas->image($src, $x, $y, $w, $h, $style->image_resolution);
- }
-
- if ($has_border_radius) {
- $this->_canvas->clipping_end();
- }
-
- if ($msg = $frame->get_image_msg()) {
- $parts = preg_split("/\s*\n\s*/", $msg);
- $height = 10;
- $_y = $alt ? $y + $h - count($parts) * $height : $y;
-
- foreach ($parts as $i => $_part) {
- $this->_canvas->text($x, $_y + $i * $height, $_part, "times", $height * 0.8, array(0.5, 0.5, 0.5));
- }
- }
-
- if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutBlocks()) {
- $this->_debug_layout($frame->get_border_box(), "blue");
- if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) {
- $this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5));
- }
- }
-
- $id = $frame->get_node()->getAttribute("id");
- if (strlen($id) > 0) {
- $this->_canvas->add_named_dest($id);
- }
- }
-}
diff --git a/library/vendor/dompdf/src/Renderer/Inline.php b/library/vendor/dompdf/src/Renderer/Inline.php
deleted file mode 100644
index 263fd6f1a..000000000
--- a/library/vendor/dompdf/src/Renderer/Inline.php
+++ /dev/null
@@ -1,211 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\Renderer;
-
-use Dompdf\Frame;
-use Dompdf\Helpers;
-
-/**
- * Renders inline frames
- *
- * @access private
- * @package dompdf
- */
-class Inline extends AbstractRenderer
-{
-
- /**
- * @param Frame $frame
- */
- function render(Frame $frame)
- {
- $style = $frame->get_style();
-
- if (!$frame->get_first_child()) {
- return; // No children, no service
- }
-
- // Draw the left border if applicable
- $bp = $style->get_border_properties();
- $widths = array(
- (float)$style->length_in_pt($bp["top"]["width"]),
- (float)$style->length_in_pt($bp["right"]["width"]),
- (float)$style->length_in_pt($bp["bottom"]["width"]),
- (float)$style->length_in_pt($bp["left"]["width"])
- );
-
- // Draw the background & border behind each child. To do this we need
- // to figure out just how much space each child takes:
- list($x, $y) = $frame->get_first_child()->get_position();
- $w = null;
- $h = 0;
- // $x += $widths[3];
- // $y += $widths[0];
-
- $this->_set_opacity($frame->get_opacity($style->opacity));
-
- $first_row = true;
-
- $DEBUGLAYOUTINLINE = $this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutInline();
-
- foreach ($frame->get_children() as $child) {
- list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
-
- if (!is_null($w) && $child_x < $x + $w) {
- //This branch seems to be supposed to being called on the first part
- //of an inline html element, and the part after the if clause for the
- //parts after a line break.
- //But because $w initially mostly is 0, and gets updated only on the next
- //round, this seem to be never executed and the common close always.
-
- // The next child is on another line. Draw the background &
- // borders on this line.
-
- // Background:
- if (($bg = $style->background_color) !== "transparent") {
- $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
- }
-
- if (($url = $style->background_image) && $url !== "none") {
- $this->_background_image($url, $x, $y, $w, $h, $style);
- }
-
- // If this is the first row, draw the left border
- if ($first_row) {
- if ($bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0) {
- $method = "_border_" . $bp["left"]["style"];
- $this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
- }
- $first_row = false;
- }
-
- // Draw the top & bottom borders
- if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0) {
- $method = "_border_" . $bp["top"]["style"];
- $this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
- }
-
- if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0) {
- $method = "_border_" . $bp["bottom"]["style"];
- $this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
- }
-
- // Handle anchors & links
- $link_node = null;
- if ($frame->get_node()->nodeName === "a") {
- $link_node = $frame->get_node();
- } else if ($frame->get_parent()->get_node()->nodeName === "a") {
- $link_node = $frame->get_parent()->get_node();
- }
-
- if ($link_node && $href = $link_node->getAttribute("href")) {
- $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href);
- $this->_canvas->add_link($href, $x, $y, $w, $h);
- }
-
- $x = $child_x;
- $y = $child_y;
- $w = (float)$child_w;
- $h = (float)$child_h;
- continue;
- }
-
- if (is_null($w)) {
- $w = (float)$child_w;
- }else {
- $w += (float)$child_w;
- }
-
- $h = max($h, $child_h);
-
- if ($DEBUGLAYOUTINLINE) {
- $this->_debug_layout($child->get_border_box(), "blue");
- if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) {
- $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5));
- }
- }
- }
-
- // Handle the last child
- if (($bg = $style->background_color) !== "transparent") {
- $this->_canvas->filled_rectangle($x + $widths[3], $y + $widths[0], $w, $h, $bg);
- }
-
- //On continuation lines (after line break) of inline elements, the style got copied.
- //But a non repeatable background image should not be repeated on the next line.
- //But removing the background image above has never an effect, and removing it below
- //removes it always, even on the initial line.
- //Need to handle it elsewhere, e.g. on certain ...clone()... usages.
- // Repeat not given: default is Style::__construct
- // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ...
- //different position? $this->_background_image($url, $x, $y, $w, $h, $style);
- if (($url = $style->background_image) && $url !== "none") {
- $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
- }
-
- // Add the border widths
- $w += (float)$widths[1] + (float)$widths[3];
- $h += (float)$widths[0] + (float)$widths[2];
-
- // make sure the border and background start inside the left margin
- $left_margin = (float)$style->length_in_pt($style->margin_left);
- $x += $left_margin;
-
- // If this is the first row, draw the left border too
- if ($first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0) {
- $method = "_border_" . $bp["left"]["style"];
- $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left");
- }
-
- // Draw the top & bottom borders
- if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0) {
- $method = "_border_" . $bp["top"]["style"];
- $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top");
- }
-
- if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0) {
- $method = "_border_" . $bp["bottom"]["style"];
- $this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
- }
-
- // Helpers::var_dump(get_class($frame->get_next_sibling()));
- // $last_row = get_class($frame->get_next_sibling()) !== 'Inline';
- // Draw the right border if this is the last row
- if ($bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0) {
- $method = "_border_" . $bp["right"]["style"];
- $this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
- }
-
- $id = $frame->get_node()->getAttribute("id");
- if (strlen($id) > 0) {
- $this->_canvas->add_named_dest($id);
- }
-
- // Only two levels of links frames
- $link_node = null;
- if ($frame->get_node()->nodeName === "a") {
- $link_node = $frame->get_node();
-
- if (($name = $link_node->getAttribute("name"))) {
- $this->_canvas->add_named_dest($name);
- }
- }
-
- if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") {
- $link_node = $frame->get_parent()->get_node();
- }
-
- // Handle anchors & links
- if ($link_node) {
- if ($href = $link_node->getAttribute("href")) {
- $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href);
- $this->_canvas->add_link($href, $x, $y, $w, $h);
- }
- }
- }
-}
diff --git a/library/vendor/dompdf/src/Renderer/TableRowGroup.php b/library/vendor/dompdf/src/Renderer/TableRowGroup.php
deleted file mode 100644
index 6742f9970..000000000
--- a/library/vendor/dompdf/src/Renderer/TableRowGroup.php
+++ /dev/null
@@ -1,50 +0,0 @@
-
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-namespace Dompdf\Renderer;
-
-use Dompdf\Frame;
-
-/**
- * Renders block frames
- *
- * @package dompdf
- */
-class TableRowGroup extends Block
-{
-
- /**
- * @param Frame $frame
- */
- function render(Frame $frame)
- {
- $style = $frame->get_style();
-
- $this->_set_opacity($frame->get_opacity($style->opacity));
-
- $this->_render_border($frame);
- $this->_render_outline($frame);
-
- if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutBlocks()) {
- $this->_debug_layout($frame->get_border_box(), "red");
- if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) {
- $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
- }
- }
-
- if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines() && $frame->get_decorator()) {
- foreach ($frame->get_decorator()->get_line_boxes() as $line) {
- $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
- }
- }
-
- $id = $frame->get_node()->getAttribute("id");
- if (strlen($id) > 0) {
- $this->_canvas->add_named_dest($id);
- }
- }
-}
diff --git a/library/vendor/dompdf/vendor/autoload.php b/library/vendor/dompdf/vendor/autoload.php
new file mode 100644
index 000000000..77629e9bf
--- /dev/null
+++ b/library/vendor/dompdf/vendor/autoload.php
@@ -0,0 +1,7 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ /** @var ?string */
+ private $vendorDir;
+
+ // PSR-4
+ /**
+ * @var array[]
+ * @psalm-var array>
+ */
+ private $prefixLengthsPsr4 = array();
+ /**
+ * @var array[]
+ * @psalm-var array>
+ */
+ private $prefixDirsPsr4 = array();
+ /**
+ * @var array[]
+ * @psalm-var array
+ */
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ /**
+ * @var array[]
+ * @psalm-var array>
+ */
+ private $prefixesPsr0 = array();
+ /**
+ * @var array[]
+ * @psalm-var array
+ */
+ private $fallbackDirsPsr0 = array();
+
+ /** @var bool */
+ private $useIncludePath = false;
+
+ /**
+ * @var string[]
+ * @psalm-var array
+ */
+ private $classMap = array();
+
+ /** @var bool */
+ private $classMapAuthoritative = false;
+
+ /**
+ * @var bool[]
+ * @psalm-var array
+ */
+ private $missingClasses = array();
+
+ /** @var ?string */
+ private $apcuPrefix;
+
+ /**
+ * @var self[]
+ */
+ private static $registeredLoaders = array();
+
+ /**
+ * @param ?string $vendorDir
+ */
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return array>
+ */
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return array
+ */
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return array
+ */
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ /**
+ * @return string[] Array of classname => path
+ * @psalm-return array
+ */
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param string[] $classMap Class to filename map
+ * @psalm-param array $classMap
+ *
+ * @return void
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param string[]|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param string[]|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param string[]|string $paths The PSR-0 base directories
+ *
+ * @return void
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param string[]|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ *
+ * @return void
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ *
+ * @return void
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ *
+ * @return void
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ *
+ * @return void
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return true|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
+ *
+ * @return self[]
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ * @private
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/library/vendor/dompdf/vendor/composer/InstalledVersions.php b/library/vendor/dompdf/vendor/composer/InstalledVersions.php
new file mode 100644
index 000000000..d50e0c9fc
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/InstalledVersions.php
@@ -0,0 +1,350 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ */
+class InstalledVersions
+{
+ /**
+ * @var mixed[]|null
+ * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null
+ */
+ private static $installed;
+
+ /**
+ * @var bool|null
+ */
+ private static $canGetVendors;
+
+ /**
+ * @var array[]
+ * @psalm-var array}>
+ */
+ private static $installedByVendor = array();
+
+ /**
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
+ *
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackages()
+ {
+ $packages = array();
+ foreach (self::getInstalled() as $installed) {
+ $packages[] = array_keys($installed['versions']);
+ }
+
+ if (1 === \count($packages)) {
+ return $packages[0];
+ }
+
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+ }
+
+ /**
+ * Returns a list of all package names with a specific type e.g. 'library'
+ *
+ * @param string $type
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackagesByType($type)
+ {
+ $packagesByType = array();
+
+ foreach (self::getInstalled() as $installed) {
+ foreach ($installed['versions'] as $name => $package) {
+ if (isset($package['type']) && $package['type'] === $type) {
+ $packagesByType[] = $name;
+ }
+ }
+ }
+
+ return $packagesByType;
+ }
+
+ /**
+ * Checks whether the given package is installed
+ *
+ * This also returns true if the package name is provided or replaced by another package
+ *
+ * @param string $packageName
+ * @param bool $includeDevRequirements
+ * @return bool
+ */
+ public static function isInstalled($packageName, $includeDevRequirements = true)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (isset($installed['versions'][$packageName])) {
+ return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package satisfies a version constraint
+ *
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+ *
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+ *
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
+ * @param string $packageName
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+ * @return bool
+ */
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
+ {
+ $constraint = $parser->parseConstraints($constraint);
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+ return $provided->matches($constraint);
+ }
+
+ /**
+ * Returns a version constraint representing all the range(s) which are installed for a given package
+ *
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+ * whether a given version of a package is installed, and not just whether it exists
+ *
+ * @param string $packageName
+ * @return string Version constraint usable with composer/semver
+ */
+ public static function getVersionRanges($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ $ranges = array();
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+ }
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+ }
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+ }
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+ }
+
+ return implode(' || ', $ranges);
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getPrettyVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['pretty_version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+ */
+ public static function getReference($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['reference'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['reference'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+ */
+ public static function getInstallPath($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @return array
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
+ */
+ public static function getRootPackage()
+ {
+ $installed = self::getInstalled();
+
+ return $installed[0]['root'];
+ }
+
+ /**
+ * Returns the raw installed.php data for custom implementations
+ *
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+ * @return array[]
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}
+ */
+ public static function getRawData()
+ {
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = include __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ return self::$installed;
+ }
+
+ /**
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
+ *
+ * @return array[]
+ * @psalm-return list}>
+ */
+ public static function getAllRawData()
+ {
+ return self::getInstalled();
+ }
+
+ /**
+ * Lets you reload the static array from another file
+ *
+ * This is only useful for complex integrations in which a project needs to use
+ * this class but then also needs to execute another project's autoloader in process,
+ * and wants to ensure both projects have access to their version of installed.php.
+ *
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
+ * the data it needs from this class, then call reload() with
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+ * the project in which it runs can then also use this class safely, without
+ * interference between PHPUnit's dependencies and the project's dependencies.
+ *
+ * @param array[] $data A vendor/composer/installed.php data set
+ * @return void
+ *
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data
+ */
+ public static function reload($data)
+ {
+ self::$installed = $data;
+ self::$installedByVendor = array();
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return list}>
+ */
+ private static function getInstalled()
+ {
+ if (null === self::$canGetVendors) {
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+ }
+
+ $installed = array();
+
+ if (self::$canGetVendors) {
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ if (isset(self::$installedByVendor[$vendorDir])) {
+ $installed[] = self::$installedByVendor[$vendorDir];
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
+ $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+ self::$installed = $installed[count($installed) - 1];
+ }
+ }
+ }
+ }
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = require __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+ $installed[] = self::$installed;
+
+ return $installed;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/composer/LICENSE b/library/vendor/dompdf/vendor/composer/LICENSE
new file mode 100644
index 000000000..f27399a04
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/library/vendor/dompdf/vendor/composer/autoload_classmap.php b/library/vendor/dompdf/vendor/composer/autoload_classmap.php
new file mode 100644
index 000000000..b708d77ba
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/autoload_classmap.php
@@ -0,0 +1,11 @@
+ $vendorDir . '/composer/InstalledVersions.php',
+ 'Dompdf\\Cpdf' => $vendorDir . '/dompdf/dompdf/lib/Cpdf.php',
+);
diff --git a/library/vendor/dompdf/vendor/composer/autoload_namespaces.php b/library/vendor/dompdf/vendor/composer/autoload_namespaces.php
new file mode 100644
index 000000000..b7fc0125d
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+ array($vendorDir . '/phenx/php-svg-lib/src/Svg'),
+ 'Sabberworm\\CSS\\' => array($vendorDir . '/sabberworm/php-css-parser/src'),
+ 'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
+ 'FontLib\\' => array($vendorDir . '/phenx/php-font-lib/src/FontLib'),
+ 'Dompdf\\' => array($vendorDir . '/dompdf/dompdf/src'),
+);
diff --git a/library/vendor/dompdf/vendor/composer/autoload_real.php b/library/vendor/dompdf/vendor/composer/autoload_real.php
new file mode 100644
index 000000000..49d5ac331
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/autoload_real.php
@@ -0,0 +1,57 @@
+= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInit47dfdbccdbe2c66869f4658d0723ed1a::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->register(true);
+
+ return $loader;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/composer/autoload_static.php b/library/vendor/dompdf/vendor/composer/autoload_static.php
new file mode 100644
index 000000000..2c05e6cb1
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/autoload_static.php
@@ -0,0 +1,66 @@
+
+ array (
+ 'Svg\\' => 4,
+ 'Sabberworm\\CSS\\' => 15,
+ ),
+ 'M' =>
+ array (
+ 'Masterminds\\' => 12,
+ ),
+ 'F' =>
+ array (
+ 'FontLib\\' => 8,
+ ),
+ 'D' =>
+ array (
+ 'Dompdf\\' => 7,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Svg\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/phenx/php-svg-lib/src/Svg',
+ ),
+ 'Sabberworm\\CSS\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/sabberworm/php-css-parser/src',
+ ),
+ 'Masterminds\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/masterminds/html5/src',
+ ),
+ 'FontLib\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/phenx/php-font-lib/src/FontLib',
+ ),
+ 'Dompdf\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/dompdf/dompdf/src',
+ ),
+ );
+
+ public static $classMap = array (
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'Dompdf\\Cpdf' => __DIR__ . '/..' . '/dompdf/dompdf/lib/Cpdf.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInit47dfdbccdbe2c66869f4658d0723ed1a::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit47dfdbccdbe2c66869f4658d0723ed1a::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInit47dfdbccdbe2c66869f4658d0723ed1a::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/composer/installed.json b/library/vendor/dompdf/vendor/composer/installed.json
new file mode 100644
index 000000000..0acb559d3
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/installed.json
@@ -0,0 +1,295 @@
+{
+ "packages": [
+ {
+ "name": "dompdf/dompdf",
+ "version": "v2.0.1",
+ "version_normalized": "2.0.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dompdf/dompdf.git",
+ "reference": "c5310df0e22c758c85ea5288175fc6cd777bc085"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dompdf/dompdf/zipball/c5310df0e22c758c85ea5288175fc6cd777bc085",
+ "reference": "c5310df0e22c758c85ea5288175fc6cd777bc085",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "masterminds/html5": "^2.0",
+ "phenx/php-font-lib": ">=0.5.4 <1.0.0",
+ "phenx/php-svg-lib": ">=0.3.3 <1.0.0",
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "ext-zip": "*",
+ "mockery/mockery": "^1.3",
+ "phpunit/phpunit": "^7.5 || ^8 || ^9",
+ "squizlabs/php_codesniffer": "^3.5"
+ },
+ "suggest": {
+ "ext-gd": "Needed to process images",
+ "ext-gmagick": "Improves image processing performance",
+ "ext-imagick": "Improves image processing performance",
+ "ext-zlib": "Needed for pdf stream compression"
+ },
+ "time": "2022-09-22T13:43:41+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Dompdf\\": "src/"
+ },
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1"
+ ],
+ "authors": [
+ {
+ "name": "The Dompdf Community",
+ "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
+ }
+ ],
+ "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
+ "homepage": "https://github.com/dompdf/dompdf",
+ "support": {
+ "issues": "https://github.com/dompdf/dompdf/issues",
+ "source": "https://github.com/dompdf/dompdf/tree/v2.0.1"
+ },
+ "install-path": "../dompdf/dompdf"
+ },
+ {
+ "name": "masterminds/html5",
+ "version": "2.7.6",
+ "version_normalized": "2.7.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Masterminds/html5-php.git",
+ "reference": "897eb517a343a2281f11bc5556d6548db7d93947"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/897eb517a343a2281f11bc5556d6548db7d93947",
+ "reference": "897eb517a343a2281f11bc5556d6548db7d93947",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7"
+ },
+ "time": "2022-08-18T16:18:26+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Masterminds\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Butcher",
+ "email": "technosophos@gmail.com"
+ },
+ {
+ "name": "Matt Farina",
+ "email": "matt@mattfarina.com"
+ },
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ }
+ ],
+ "description": "An HTML5 parser and serializer.",
+ "homepage": "http://masterminds.github.io/html5-php",
+ "keywords": [
+ "HTML5",
+ "dom",
+ "html",
+ "parser",
+ "querypath",
+ "serializer",
+ "xml"
+ ],
+ "support": {
+ "issues": "https://github.com/Masterminds/html5-php/issues",
+ "source": "https://github.com/Masterminds/html5-php/tree/2.7.6"
+ },
+ "install-path": "../masterminds/html5"
+ },
+ {
+ "name": "phenx/php-font-lib",
+ "version": "0.5.4",
+ "version_normalized": "0.5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dompdf/php-font-lib.git",
+ "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/dd448ad1ce34c63d09baccd05415e361300c35b4",
+ "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^3 || ^4 || ^5"
+ },
+ "time": "2021-12-17T19:44:54+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "FontLib\\": "src/FontLib"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Ménager",
+ "email": "fabien.menager@gmail.com"
+ }
+ ],
+ "description": "A library to read, parse, export and make subsets of different types of font files.",
+ "homepage": "https://github.com/PhenX/php-font-lib",
+ "support": {
+ "issues": "https://github.com/dompdf/php-font-lib/issues",
+ "source": "https://github.com/dompdf/php-font-lib/tree/0.5.4"
+ },
+ "install-path": "../phenx/php-font-lib"
+ },
+ {
+ "name": "phenx/php-svg-lib",
+ "version": "0.5.0",
+ "version_normalized": "0.5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dompdf/php-svg-lib.git",
+ "reference": "76876c6cf3080bcb6f249d7d59705108166a6685"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/76876c6cf3080bcb6f249d7d59705108166a6685",
+ "reference": "76876c6cf3080bcb6f249d7d59705108166a6685",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": "^7.1 || ^8.0",
+ "sabberworm/php-css-parser": "^8.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
+ },
+ "time": "2022-09-06T12:16:56+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Svg\\": "src/Svg"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Ménager",
+ "email": "fabien.menager@gmail.com"
+ }
+ ],
+ "description": "A library to read, parse and export to PDF SVG files.",
+ "homepage": "https://github.com/PhenX/php-svg-lib",
+ "support": {
+ "issues": "https://github.com/dompdf/php-svg-lib/issues",
+ "source": "https://github.com/dompdf/php-svg-lib/tree/0.5.0"
+ },
+ "install-path": "../phenx/php-svg-lib"
+ },
+ {
+ "name": "sabberworm/php-css-parser",
+ "version": "8.4.0",
+ "version_normalized": "8.4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
+ "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30",
+ "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=5.6.20"
+ },
+ "require-dev": {
+ "codacy/coverage": "^1.4",
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "suggest": {
+ "ext-mbstring": "for parsing UTF-8 CSS"
+ },
+ "time": "2021-12-11T13:40:54+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Sabberworm\\CSS\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Raphael Schweikert"
+ }
+ ],
+ "description": "Parser for CSS Files written in PHP",
+ "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
+ "keywords": [
+ "css",
+ "parser",
+ "stylesheet"
+ ],
+ "support": {
+ "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues",
+ "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0"
+ },
+ "install-path": "../sabberworm/php-css-parser"
+ }
+ ],
+ "dev": true,
+ "dev-package-names": []
+}
diff --git a/library/vendor/dompdf/vendor/composer/installed.php b/library/vendor/dompdf/vendor/composer/installed.php
new file mode 100644
index 000000000..cdf63de96
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/installed.php
@@ -0,0 +1,68 @@
+ array(
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'type' => 'project',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'reference' => NULL,
+ 'name' => '__root__',
+ 'dev' => true,
+ ),
+ 'versions' => array(
+ '__root__' => array(
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'type' => 'project',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'reference' => NULL,
+ 'dev_requirement' => false,
+ ),
+ 'dompdf/dompdf' => array(
+ 'pretty_version' => 'v2.0.1',
+ 'version' => '2.0.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../dompdf/dompdf',
+ 'aliases' => array(),
+ 'reference' => 'c5310df0e22c758c85ea5288175fc6cd777bc085',
+ 'dev_requirement' => false,
+ ),
+ 'masterminds/html5' => array(
+ 'pretty_version' => '2.7.6',
+ 'version' => '2.7.6.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../masterminds/html5',
+ 'aliases' => array(),
+ 'reference' => '897eb517a343a2281f11bc5556d6548db7d93947',
+ 'dev_requirement' => false,
+ ),
+ 'phenx/php-font-lib' => array(
+ 'pretty_version' => '0.5.4',
+ 'version' => '0.5.4.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../phenx/php-font-lib',
+ 'aliases' => array(),
+ 'reference' => 'dd448ad1ce34c63d09baccd05415e361300c35b4',
+ 'dev_requirement' => false,
+ ),
+ 'phenx/php-svg-lib' => array(
+ 'pretty_version' => '0.5.0',
+ 'version' => '0.5.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../phenx/php-svg-lib',
+ 'aliases' => array(),
+ 'reference' => '76876c6cf3080bcb6f249d7d59705108166a6685',
+ 'dev_requirement' => false,
+ ),
+ 'sabberworm/php-css-parser' => array(
+ 'pretty_version' => '8.4.0',
+ 'version' => '8.4.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../sabberworm/php-css-parser',
+ 'aliases' => array(),
+ 'reference' => 'e41d2140031d533348b2192a83f02d8dd8a71d30',
+ 'dev_requirement' => false,
+ ),
+ ),
+);
diff --git a/library/vendor/dompdf/vendor/composer/platform_check.php b/library/vendor/dompdf/vendor/composer/platform_check.php
new file mode 100644
index 000000000..6d3407dbb
--- /dev/null
+++ b/library/vendor/dompdf/vendor/composer/platform_check.php
@@ -0,0 +1,26 @@
+= 70100)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.0". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+ if (!headers_sent()) {
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ if (!ini_get('display_errors')) {
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+ } elseif (!headers_sent()) {
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+ }
+ }
+ trigger_error(
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
+ E_USER_ERROR
+ );
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/AUTHORS.md b/library/vendor/dompdf/vendor/dompdf/dompdf/AUTHORS.md
new file mode 100644
index 000000000..686147928
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/AUTHORS.md
@@ -0,0 +1,24 @@
+Dompdf was designed and developed by Benj Carson.
+
+### Current Team
+
+* **Brian Sweeney** (maintainer)
+* **Till Berger**
+
+### Alumni
+
+* **Benj Carson** (creator)
+* **Fabien Ménager**
+* **Simon Berger**
+* **Orion Richardson**
+
+### Contributors
+* **Gabriel Bull**
+* **Barry vd. Heuvel**
+* **Ryan H. Masten**
+* **Helmut Tischer**
+* [and many more...](https://github.com/dompdf/dompdf/graphs/contributors)
+
+### Thanks
+
+Dompdf would not have been possible without strong community support.
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/LICENSE.LGPL b/library/vendor/dompdf/vendor/dompdf/dompdf/LICENSE.LGPL
new file mode 100644
index 000000000..6ef5de82a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/LICENSE.LGPL
@@ -0,0 +1,456 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
\ No newline at end of file
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/README.md b/library/vendor/dompdf/vendor/dompdf/dompdf/README.md
new file mode 100644
index 000000000..7546e807e
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/README.md
@@ -0,0 +1,232 @@
+Dompdf
+======
+
+[](https://github.com/dompdf/dompdf/actions/workflows/test.yml)
+[](https://packagist.org/packages/dompdf/dompdf)
+[](https://packagist.org/packages/dompdf/dompdf)
+[](https://packagist.org/packages/dompdf/dompdf)
+
+**Dompdf is an HTML to PDF converter**
+
+At its heart, dompdf is (mostly) a [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant
+HTML layout and rendering engine written in PHP. It is a style-driven renderer:
+it will download and read external stylesheets, inline style tags, and the style
+attributes of individual HTML elements. It also supports most presentational
+HTML attributes.
+
+*This document applies to the latest stable code which may not reflect the current
+release. For released code please
+[navigate to the appropriate tag](https://github.com/dompdf/dompdf/tags).*
+
+----
+
+**Check out the [demo](http://eclecticgeek.com/dompdf/debug.php) and ask any
+question on [StackOverflow](https://stackoverflow.com/questions/tagged/dompdf) or
+in [Discussions](https://github.com/dompdf/dompdf/discussions).**
+
+Follow us on [](http://www.twitter.com/dompdf).
+
+---
+
+
+
+## Features
+
+ * Handles most CSS 2.1 and a few CSS3 properties, including @import, @media &
+ @page rules
+ * Supports most presentational HTML 4.0 attributes
+ * Supports external stylesheets, either local or through http/ftp (via
+ fopen-wrappers)
+ * Supports complex tables, including row & column spans, separate & collapsed
+ border models, individual cell styling
+ * Image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
+ * No dependencies on external PDF libraries, thanks to the R&OS PDF class
+ * Inline PHP support
+ * Basic SVG support (see "Limitations" below)
+
+## Requirements
+
+ * PHP version 7.1 or higher
+ * DOM extension
+ * MBString extension
+ * php-font-lib
+ * php-svg-lib
+
+Note that some required dependencies may have further dependencies
+(notably php-svg-lib requires sabberworm/php-css-parser).
+
+### Recommendations
+
+ * OPcache (OPcache, XCache, APC, etc.): improves performance
+ * GD (for image processing)
+ * IMagick or GMagick extension: improves image processing performance
+
+Visit the wiki for more information:
+https://github.com/dompdf/dompdf/wiki/Requirements
+
+## About Fonts & Character Encoding
+
+PDF documents internally support the following fonts: Helvetica, Times-Roman,
+Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI
+encoding. In order for a PDF to display characters that are not available in
+Windows ANSI, you must supply an external font. Dompdf will embed any referenced
+font in the PDF so long as it has been pre-loaded or is accessible to dompdf and
+reference in CSS @font-face rules. See the
+[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding)
+for more information on how to use fonts.
+
+The [DejaVu TrueType fonts](https://dejavu-fonts.github.io/) have been pre-installed
+to give dompdf decent Unicode character coverage by default. To use the DejaVu
+fonts reference the font in your stylesheet, e.g. `body { font-family: DejaVu
+Sans; }` (for DejaVu Sans). The following DejaVu 2.34 fonts are available:
+DejaVu Sans, DejaVu Serif, and DejaVu Sans Mono.
+
+## Easy Installation
+
+### Install with composer
+
+To install with [Composer](https://getcomposer.org/), simply require the
+latest version of this package.
+
+```bash
+composer require dompdf/dompdf
+```
+
+Make sure that the autoload file from Composer is loaded.
+
+```php
+// somewhere early in your project's loading, require the Composer autoloader
+// see: http://getcomposer.org/doc/00-intro.md
+require 'vendor/autoload.php';
+
+```
+
+### Download and install
+
+Download a packaged archive of dompdf and extract it into the
+directory where dompdf will reside
+
+ * You can download stable copies of dompdf from
+ https://github.com/dompdf/dompdf/releases
+ * Or download a nightly (the latest, unreleased code) from
+ http://eclecticgeek.com/dompdf
+
+Use the packaged release autoloader to load dompdf, libraries,
+and helper functions in your PHP:
+
+```php
+// include autoloader
+require_once 'dompdf/autoload.inc.php';
+```
+
+Note: packaged releases are named according using semantic
+versioning (_dompdf_MAJOR-MINOR-PATCH.zip_). So the 1.0.0
+release would be dompdf_1-0-0.zip. This is the only download
+that includes the autoloader for Dompdf and all its dependencies.
+
+### Install with git
+
+From the command line, switch to the directory where dompdf will
+reside and run the following commands:
+
+```sh
+git clone https://github.com/dompdf/dompdf.git
+cd dompdf/lib
+
+git clone https://github.com/PhenX/php-font-lib.git php-font-lib
+cd php-font-lib
+git checkout 0.5.1
+cd ..
+
+git clone https://github.com/PhenX/php-svg-lib.git php-svg-lib
+cd php-svg-lib
+git checkout v0.3.2
+cd ..
+
+git clone https://github.com/sabberworm/PHP-CSS-Parser.git php-css-parser
+cd php-css-parser
+git checkout 8.1.0
+```
+
+Require dompdf and it's dependencies in your PHP.
+For details see the [autoloader in the utils project](https://github.com/dompdf/utils/blob/master/autoload.inc.php).
+
+## Quick Start
+
+Just pass your HTML in to dompdf and stream the output:
+
+```php
+// reference the Dompdf namespace
+use Dompdf\Dompdf;
+
+// instantiate and use the dompdf class
+$dompdf = new Dompdf();
+$dompdf->loadHtml('hello world');
+
+// (Optional) Setup the paper size and orientation
+$dompdf->setPaper('A4', 'landscape');
+
+// Render the HTML as PDF
+$dompdf->render();
+
+// Output the generated PDF to Browser
+$dompdf->stream();
+```
+
+### Setting Options
+
+Set options during dompdf instantiation:
+
+```php
+use Dompdf\Dompdf;
+use Dompdf\Options;
+
+$options = new Options();
+$options->set('defaultFont', 'Courier');
+$dompdf = new Dompdf($options);
+```
+
+or at run time
+
+```php
+use Dompdf\Dompdf;
+
+$dompdf = new Dompdf();
+$options = $dompdf->getOptions();
+$options->setDefaultFont('Courier');
+$dompdf->setOptions($options);
+```
+
+See [Dompdf\Options](src/Options.php) for a list of available options.
+
+### Resource Reference Requirements
+
+In order to protect potentially sensitive information Dompdf imposes
+restrictions on files referenced from the local file system or the web.
+
+Files accessed through web-based protocols have the following requirements:
+ * The Dompdf option "isRemoteEnabled" must be set to "true"
+ * PHP must either have the curl extension enabled or the
+ allow_url_fopen setting set to true
+
+Files accessed through the local file system have the following requirement:
+ * The file must fall within the path(s) specified for the Dompdf "chroot" option
+
+## Limitations (Known Issues)
+
+ * Table cells are not pageable, meaning a table row must fit on a single page.
+ * Elements are rendered on the active page when they are parsed.
+ * Embedding "raw" SVG's (` `) isn't working yet, you need to
+ either link to an external SVG file, or use a DataURI like this:
+ ```php
+ $html = ' ';
+ ```
+ Watch https://github.com/dompdf/dompdf/issues/320 for progress
+ * Does not support CSS flexbox.
+ * Does not support CSS Grid.
+---
+
+[](http://goo.gl/DSvWf)
+
+*If you find this project useful, please consider making a donation.
+Any funds donated will be used to help further development on this project.)*
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/VERSION b/library/vendor/dompdf/vendor/dompdf/dompdf/VERSION
new file mode 100644
index 000000000..38f77a65b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/VERSION
@@ -0,0 +1 @@
+2.0.1
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/composer.json b/library/vendor/dompdf/vendor/dompdf/dompdf/composer.json
new file mode 100644
index 000000000..268a81e7b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/composer.json
@@ -0,0 +1,47 @@
+{
+ "name": "dompdf/dompdf",
+ "type": "library",
+ "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
+ "homepage": "https://github.com/dompdf/dompdf",
+ "license": "LGPL-2.1",
+ "authors": [
+ {
+ "name": "The Dompdf Community",
+ "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "Dompdf\\": "src/"
+ },
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Dompdf\\Tests\\": "tests/"
+ }
+ },
+ "require": {
+ "php": "^7.1 || ^8.0",
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "masterminds/html5": "^2.0",
+ "phenx/php-font-lib": ">=0.5.4 <1.0.0",
+ "phenx/php-svg-lib": ">=0.3.3 <1.0.0"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "ext-zip": "*",
+ "phpunit/phpunit": "^7.5 || ^8 || ^9",
+ "squizlabs/php_codesniffer": "^3.5",
+ "mockery/mockery": "^1.3"
+ },
+ "suggest": {
+ "ext-gd": "Needed to process images",
+ "ext-imagick": "Improves image processing performance",
+ "ext-gmagick": "Improves image processing performance",
+ "ext-zlib": "Needed for pdf stream compression"
+ }
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/CPdf.php b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/Cpdf.php
similarity index 60%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/CPdf.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/Cpdf.php
index 2dce8f395..27bfa2981 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/CPdf.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/Cpdf.php
@@ -3,23 +3,41 @@
* A PHP class to provide the basic functionality to create a pdf document without
* any requirement for additional modules.
*
- * Extended by Orion Richardson to support Unicode / UTF-8 characters using
- * TCPDF and others as a guide.
- *
- * @author Wayne Munro
- * @author Orion Richardson
- * @author Helmut Tischer
- * @author Ryan H. Masten
- * @author Brian Sweeney
- * @author Fabien Ménager
- * @license Public Domain http://creativecommons.org/licenses/publicdomain/
+ * @author Wayne Munro
+ * @license http://creativecommons.org/licenses/publicdomain/ Public Domain
* @package Cpdf
*/
+namespace Dompdf;
-namespace Svg\Surface;
+use FontLib\Exception\FontNotFoundException;
+use FontLib\Font;
+use FontLib\BinaryStream;
-class CPdf
+class Cpdf
{
+ const PDF_VERSION = '1.7';
+
+ const ACROFORM_SIG_SIGNATURESEXISTS = 0x0001;
+ const ACROFORM_SIG_APPENDONLY = 0x0002;
+
+ const ACROFORM_FIELD_BUTTON = 'Btn';
+ const ACROFORM_FIELD_TEXT = 'Tx';
+ const ACROFORM_FIELD_CHOICE = 'Ch';
+ const ACROFORM_FIELD_SIG = 'Sig';
+
+ const ACROFORM_FIELD_READONLY = 0x0001;
+ const ACROFORM_FIELD_REQUIRED = 0x0002;
+
+ const ACROFORM_FIELD_TEXT_MULTILINE = 0x1000;
+ const ACROFORM_FIELD_TEXT_PASSWORD = 0x2000;
+ const ACROFORM_FIELD_TEXT_RICHTEXT = 0x10000;
+
+ const ACROFORM_FIELD_CHOICE_COMBO = 0x20000;
+ const ACROFORM_FIELD_CHOICE_EDIT = 0x40000;
+ const ACROFORM_FIELD_CHOICE_SORT = 0x80000;
+ const ACROFORM_FIELD_CHOICE_MULTISELECT = 0x200000;
+
+ const XOBJECT_SUBTYPE_FORM = 'Form';
/**
* @var integer The current number of pdf objects in the document
@@ -29,18 +47,40 @@ class CPdf
/**
* @var array This array contains all of the pdf objects, ready for final assembly
*/
- public $objects = array();
+ public $objects = [];
/**
* @var integer The objectId (number within the objects array) of the document catalog
*/
public $catalogId;
+ /**
+ * @var integer The objectId (number within the objects array) of indirect references (Javascript EmbeddedFiles)
+ */
+ protected $indirectReferenceId = 0;
+
+ /**
+ * @var integer The objectId (number within the objects array)
+ */
+ protected $embeddedFilesId = 0;
+
+ /**
+ * AcroForm objectId
+ *
+ * @var integer
+ */
+ public $acroFormId;
+
+ /**
+ * @var int
+ */
+ public $signatureMaxLen = 5000;
+
/**
* @var array Array carrying information about the fonts that the system currently knows about
* Used to ensure that a font is not loaded twice, among other things
*/
- public $fonts = array();
+ public $fonts = [];
/**
* @var string The default font metrics file to use if no other font has been loaded.
@@ -88,22 +128,27 @@ class CPdf
*/
private $numStates = 0;
+ /**
+ * @var array Number of graphic state resources used
+ */
+ private $gstates = [];
+
/**
* @var array Current color for fill operations, defaults to inactive value,
* all three components should be between 0 and 1 inclusive when active
*/
public $currentColor = null;
- /**
- * @var string Fill rule (nonzero or evenodd)
- */
- public $fillRule = "nonzero";
-
/**
* @var array Current color for stroke operations (lines etc.)
*/
public $currentStrokeColor = null;
+ /**
+ * @var string Fill rule (nonzero or evenodd)
+ */
+ public $fillRule = "nonzero";
+
/**
* @var string Current style that lines are drawn in
*/
@@ -112,18 +157,18 @@ class CPdf
/**
* @var array Current line transparency (partial graphics state)
*/
- public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
+ public $currentLineTransparency = ["mode" => "Normal", "opacity" => 1.0];
/**
* array Current fill transparency (partial graphics state)
*/
- public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
+ public $currentFillTransparency = ["mode" => "Normal", "opacity" => 1.0];
/**
* @var array An array which is used to save the state of the document, mainly the colors and styles
- * it is used to temporarily change to another state, the change back to what it was before
+ * it is used to temporarily change to another state, then change back to what it was before
*/
- public $stateStack = array();
+ public $stateStack = [];
/**
* @var integer Number of elements within the state stack
@@ -138,7 +183,7 @@ class CPdf
/**
* @var array Object Id storage stack
*/
- public $stack = array();
+ public $stack = [];
/**
* @var integer Number of elements within the object Id storage stack
@@ -149,12 +194,12 @@ class CPdf
* an array which contains information about the objects which are not firmly attached to pages
* these have been added with the addObject function
*/
- public $looseObjects = array();
+ public $looseObjects = [];
/**
- * array contains infomation about how the loose objects are to be added to the document
+ * array contains information about how the loose objects are to be added to the document
*/
- public $addLooseObjects = array();
+ public $addLooseObjects = [];
/**
* @var integer The objectId of the information object for the document
@@ -171,25 +216,13 @@ class CPdf
* @var array An array containing options about the document
* it defaults to turning on the compression of the objects
*/
- public $options = array('compression' => true);
+ public $options = ['compression' => true];
/**
* @var integer The objectId of the first page of the document
*/
public $firstPageId;
- /**
- * @var float Used to track the last used value of the inter-word spacing, this is so that it is known
- * when the spacing is changed.
- */
- public $wordSpaceAdjust = 0;
-
- /**
- * @var float Used to track the last used value of the inter-letter spacing, this is so that it is known
- * when the spacing is changed.
- */
- public $charSpaceAdjust = 0;
-
/**
* @var integer The object Id of the procset object
*/
@@ -198,9 +231,9 @@ class CPdf
/**
* @var array Store the information about the relationship between font families
* this used so that the code knows which font is the bold version of another font, etc.
- * the value of this array is initialised in the constuctor function.
+ * the value of this array is initialised in the constructor function.
*/
- public $fontFamilies = array();
+ public $fontFamilies = [];
/**
* @var string Folder for php serialized formats of font metrics files.
@@ -219,9 +252,8 @@ class CPdf
/**
* @var string Temporary folder.
- * If empty string, will attempty system tmp folder.
+ * If empty string, will attempt system tmp folder.
* This can be passed in from class creator.
- * Only used for conversion of gd images to jpeg images.
*/
public $tmp = '';
@@ -236,7 +268,7 @@ class CPdf
public $messages = '';
/**
- * @var string The ancryption array for the document encryption is stored here
+ * @var string The encryption array for the document encryption is stored here
*/
public $arc4 = '';
@@ -263,7 +295,7 @@ class CPdf
/**
* @var array Array which forms a stack to keep track of nested callback functions
*/
- public $callback = array();
+ public $callback = [];
/**
* @var integer The number of callback functions in the callback array
@@ -274,7 +306,7 @@ class CPdf
* @var array Store label->id pairs for named destinations, these will be used to replace internal links
* done this way so that destinations can be defined after the location that links to them
*/
- public $destinations = array();
+ public $destinations = [];
/**
* @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
@@ -287,7 +319,17 @@ class CPdf
* @var array Table of Image origin filenames and image labels which were already added with o_image().
* Allows to merge identical images
*/
- public $imagelist = array();
+ public $imagelist = [];
+
+ /**
+ * @var array Table of already added alpha and plain image files for transparent PNG images.
+ */
+ protected $imageAlphaList = [];
+
+ /**
+ * @var array List of temporary image files to be deleted after processing.
+ */
+ protected $imageCache = [];
/**
* @var boolean Whether the text passed in should be treated as Unicode or just local character set.
@@ -307,22 +349,27 @@ class CPdf
/**
* @var array Current page size
*/
- protected $currentPageSize = array("width" => 0, "height" => 0);
+ protected $currentPageSize = ["width" => 0, "height" => 0];
/**
* @var array All the chars that will be required in the font subsets
*/
- protected $stringSubsets = array();
+ protected $stringSubsets = [];
/**
* @var string The target internal encoding
*/
- static protected $targetEncoding = 'iso-8859-1';
+ protected static $targetEncoding = 'Windows-1252';
+
+ /**
+ * @var array
+ */
+ protected $byteRange = array();
/**
* @var array The list of the core fonts
*/
- static protected $coreFonts = array(
+ protected static $coreFonts = [
'courier',
'courier-bold',
'courier-oblique',
@@ -337,7 +384,7 @@ class CPdf
'times-bolditalic',
'symbol',
'zapfdingbats'
- );
+ ];
/**
* Class constructor
@@ -348,11 +395,11 @@ class CPdf
* @param string $fontcache The font cache folder
* @param string $tmp The temporary folder
*/
- function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '')
+ function __construct($pageSize = [0, 0, 612, 792], $isUnicode = false, $fontcache = '', $tmp = '')
{
$this->isUnicode = $isUnicode;
- $this->fontcache = $fontcache;
- $this->tmp = $tmp;
+ $this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\");
+ $this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir());
$this->newDocument($pageSize);
$this->compressionReady = function_exists('gzcompress');
@@ -363,7 +410,15 @@ class CPdf
// also initialize the font families that are known about already
$this->setFontFamily('init');
- // $this->fileIdentifier = md5('xxxxxxxx'.time());
+ }
+
+ public function __destruct()
+ {
+ foreach ($this->imageCache as $file) {
+ if (file_exists($file)) {
+ unlink($file);
+ }
+ }
}
/**
@@ -383,24 +438,27 @@ class CPdf
/**
* Destination object, used to specify the location for the user to jump to, presently on opening
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return string|null
*/
protected function o_destination($id, $action, $options = '')
{
- if ($action !== 'new') {
- $o = &$this->objects[$id];
- }
-
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'destination', 'info' => array());
+ $this->objects[$id] = ['t' => 'destination', 'info' => []];
$tmp = '';
switch ($options['type']) {
case 'XYZ':
+ /** @noinspection PhpMissingBreakStatementInspection */
case 'FitR':
$tmp = ' ' . $options['p3'] . $tmp;
case 'FitH':
case 'FitV':
case 'FitBH':
+ /** @noinspection PhpMissingBreakStatementInspection */
case 'FitBV':
$tmp = ' ' . $options['p1'] . ' ' . $options['p2'] . $tmp;
case 'Fit':
@@ -412,56 +470,133 @@ class CPdf
break;
case 'out':
+ $o = &$this->objects[$id];
+
$tmp = $o['info'];
$res = "\n$id 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj";
return $res;
}
+
+ return null;
}
/**
* set the viewer preferences
+ *
+ * @param $id
+ * @param $action
+ * @param string|array $options
+ * @return string|null
*/
protected function o_viewerPreferences($id, $action, $options = '')
{
- if ($action !== 'new') {
- $o = &$this->objects[$id];
- }
-
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
+ $this->objects[$id] = ['t' => 'viewerPreferences', 'info' => []];
break;
case 'add':
+ $o = &$this->objects[$id];
+
foreach ($options as $k => $v) {
switch ($k) {
+ // Boolean keys
case 'HideToolbar':
case 'HideMenubar':
case 'HideWindowUI':
case 'FitWindow':
case 'CenterWindow':
+ case 'DisplayDocTitle':
+ case 'PickTrayByPDFSize':
+ $o['info'][$k] = (bool)$v;
+ break;
+
+ // Integer keys
+ case 'NumCopies':
+ $o['info'][$k] = (int)$v;
+ break;
+
+ // Name keys
+ case 'ViewArea':
+ case 'ViewClip':
+ case 'PrintClip':
+ case 'PrintArea':
+ $o['info'][$k] = (string)$v;
+ break;
+
+ // Named with limited valid values
case 'NonFullScreenPageMode':
- case 'Direction':
+ if (!in_array($v, ['UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'])) {
+ break;
+ }
$o['info'][$k] = $v;
break;
+
+ case 'Direction':
+ if (!in_array($v, ['L2R', 'R2L'])) {
+ break;
+ }
+ $o['info'][$k] = $v;
+ break;
+
+ case 'PrintScaling':
+ if (!in_array($v, ['None', 'AppDefault'])) {
+ break;
+ }
+ $o['info'][$k] = $v;
+ break;
+
+ case 'Duplex':
+ if (!in_array($v, ['None', 'Simplex', 'DuplexFlipShortEdge', 'DuplexFlipLongEdge'])) {
+ break;
+ }
+ $o['info'][$k] = $v;
+ break;
+
+ // Integer array
+ case 'PrintPageRange':
+ // Cast to integer array
+ foreach ($v as $vK => $vV) {
+ $v[$vK] = (int)$vV;
+ }
+ $o['info'][$k] = array_values($v);
+ break;
}
}
break;
case 'out':
+ $o = &$this->objects[$id];
$res = "\n$id 0 obj\n<< ";
+
foreach ($o['info'] as $k => $v) {
+ if (is_string($v)) {
+ $v = '/' . $v;
+ } elseif (is_int($v)) {
+ $v = (string) $v;
+ } elseif (is_bool($v)) {
+ $v = ($v ? 'true' : 'false');
+ } elseif (is_array($v)) {
+ $v = '[' . implode(' ', $v) . ']';
+ }
$res .= "\n/$k $v";
}
- $res .= "\n>>\n";
+ $res .= "\n>>\nendobj";
return $res;
}
+
+ return null;
}
/**
* define the document catalog, the overall controller for the document
+ *
+ * @param $id
+ * @param $action
+ * @param string|array $options
+ * @return string|null
*/
protected function o_catalog($id, $action, $options = '')
{
@@ -471,14 +606,15 @@ class CPdf
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'catalog', 'info' => array());
+ $this->objects[$id] = ['t' => 'catalog', 'info' => []];
$this->catalogId = $id;
break;
+ case 'acroform':
case 'outlines':
case 'pages':
case 'openHere':
- case 'javascript':
+ case 'names':
$o['info'][$action] = $options;
break;
@@ -515,8 +651,12 @@ class CPdf
$res .= "\n/OpenAction $v 0 R";
break;
- case 'javascript':
- $res .= "\n/Names <>";
+ case 'names':
+ $res .= "\n/Names $v 0 R";
+ break;
+
+ case 'acroform':
+ $res .= "\n/AcroForm $v 0 R";
break;
}
}
@@ -525,10 +665,17 @@ class CPdf
return $res;
}
+
+ return null;
}
/**
* object which is a parent to the pages in the document
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return string|null
*/
protected function o_pages($id, $action, $options = '')
{
@@ -538,7 +685,7 @@ class CPdf
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'pages', 'info' => array());
+ $this->objects[$id] = ['t' => 'pages', 'info' => []];
$this->o_catalog($this->catalogId, 'pages', $id);
break;
@@ -588,19 +735,19 @@ class CPdf
case 'mediaBox':
$o['info']['mediaBox'] = $options;
// which should be an array of 4 numbers
- $this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
+ $this->currentPageSize = ['width' => $options[2], 'height' => $options[3]];
break;
case 'font':
- $o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
+ $o['info']['fonts'][] = ['objNum' => $options['objNum'], 'fontNum' => $options['fontNum']];
break;
case 'extGState':
- $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
+ $o['info']['extGStates'][] = ['objNum' => $options['objNum'], 'stateNum' => $options['stateNum']];
break;
case 'xObject':
- $o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
+ $o['info']['xObjects'][] = ['objNum' => $options['objNum'], 'label' => $options['label']];
break;
case 'out':
@@ -666,10 +813,17 @@ class CPdf
return $res;
}
+
+ return null;
}
/**
* define the outlines in the doc, empty for now
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return string|null
*/
protected function o_outlines($id, $action, $options = '')
{
@@ -679,7 +833,7 @@ class CPdf
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
+ $this->objects[$id] = ['t' => 'outlines', 'info' => ['outlines' => []]];
$this->o_catalog($this->catalogId, 'outlines', $id);
break;
@@ -701,10 +855,18 @@ class CPdf
return $res;
}
+
+ return null;
}
/**
* an object to hold the font description
+ *
+ * @param $id
+ * @param $action
+ * @param string|array $options
+ * @return string|null
+ * @throws FontNotFoundException
*/
protected function o_font($id, $action, $options = '')
{
@@ -714,14 +876,15 @@ class CPdf
switch ($action) {
case 'new':
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'font',
- 'info' => array(
+ 'info' => [
'name' => $options['name'],
'fontFileName' => $options['fontFileName'],
- 'SubType' => 'Type1'
- )
- );
+ 'SubType' => 'Type1',
+ 'isSubsetting' => $options['isSubsetting']
+ ]
+ ];
$fontNum = $this->numFonts;
$this->objects[$id]['info']['fontNum'] = $fontNum;
@@ -757,72 +920,48 @@ class CPdf
// For Unicode fonts, we need to incorporate font data into
// sub-sections that are linked from the primary font section.
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
- // for more informaiton.
+ // for more information.
//
// All of this code is adapted from the excellent changes made to
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
$toUnicodeId = ++$this->numObj;
- $this->o_contents($toUnicodeId, 'new', 'raw');
+ $this->o_toUnicode($toUnicodeId, 'new');
$this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
- $stream = <<> def
-/CMapName /Adobe-Identity-UCS def
-/CMapType 2 def
-1 begincodespacerange
-<0000>
-endcodespacerange
-1 beginbfrange
-<0000> <0000>
-endbfrange
-endcmap
-CMapName currentdict /CMap defineresource pop
-end
-end
-EOT;
-
- $res = "<>\n";
- $res .= "stream\n" . $stream . "endstream";
-
- $this->objects[$toUnicodeId]['c'] = $res;
-
$cidFontId = ++$this->numObj;
$this->o_fontDescendentCID($cidFontId, 'new', $options);
$this->objects[$id]['info']['cidFont'] = $cidFontId;
}
// also tell the pages node about the new font
- $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
+ $this->o_pages($this->currentNode, 'font', ['fontNum' => $fontNum, 'objNum' => $id]);
break;
case 'add':
- foreach ($options as $k => $v) {
- switch ($k) {
- case 'BaseFont':
- $o['info']['name'] = $v;
- break;
- case 'FirstChar':
- case 'LastChar':
- case 'Widths':
- case 'FontDescriptor':
- case 'SubType':
- $this->addMessage('o_font ' . $k . " : " . $v);
- $o['info'][$k] = $v;
- break;
- }
- }
+ $font_options = $this->processFont($id, $o['info']);
- // pass values down to descendent font
- if (isset($o['info']['cidFont'])) {
- $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
+ if ($font_options !== false) {
+ foreach ($font_options as $k => $v) {
+ switch ($k) {
+ case 'BaseFont':
+ $o['info']['name'] = $v;
+ break;
+ case 'FirstChar':
+ case 'LastChar':
+ case 'Widths':
+ case 'FontDescriptor':
+ case 'SubType':
+ $this->addMessage('o_font ' . $k . " : " . $v);
+ $o['info'][$k] = $v;
+ break;
+ }
+ }
+
+ // pass values down to descendent font
+ if (isset($o['info']['cidFont'])) {
+ $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $font_options);
+ }
}
break;
@@ -831,7 +970,7 @@ EOT;
// For Unicode fonts, we need to incorporate font data into
// sub-sections that are linked from the primary font section.
// Look at o_fontGIDtoCID and o_fontDescendentCID functions
- // for more informaiton.
+ // for more information.
//
// All of this code is adapted from the excellent changes made to
// transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
@@ -883,10 +1022,371 @@ EOT;
return $res;
}
+
+ return null;
+ }
+
+ protected function getFontSubsettingTag(array $font): string
+ {
+ // convert font num to hexavigesimal numeral system letters A - Z only
+ $base_26 = strtoupper(base_convert($font['fontNum'], 10, 26));
+ for ($i = 0; $i < strlen($base_26); $i++) {
+ $char = $base_26[$i];
+ if ($char <= "9") {
+ $base_26[$i] = chr(65 + intval($char));
+ } else {
+ $base_26[$i] = chr(ord($char) + 10);
+ }
+ }
+
+ return 'SUB' . str_pad($base_26, 3, 'A', STR_PAD_LEFT);
+ }
+
+ /**
+ * @param int $fontObjId
+ * @param array $object_info
+ * @return array|false
+ * @throws FontNotFoundException
+ */
+ private function processFont(int $fontObjId, array $object_info)
+ {
+ $fontFileName = $object_info['fontFileName'];
+ if (!isset($this->fonts[$fontFileName])) {
+ return false;
+ }
+
+ $font = &$this->fonts[$fontFileName];
+
+ $fileSuffix = $font['fileSuffix'];
+ $fileSuffixLower = strtolower($font['fileSuffix']);
+ $fbfile = "$fontFileName.$fileSuffix";
+ $isTtfFont = $fileSuffixLower === 'ttf';
+ $isPfbFont = $fileSuffixLower === 'pfb';
+
+ $this->addMessage('selectFont: checking for - ' . $fbfile);
+
+ if (!$fileSuffix) {
+ $this->addMessage(
+ 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
+ );
+
+ return false;
+ } else {
+ $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
+ // $fontObj = $this->numObj;
+ $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
+
+ // find the array of font widths, and put that into an object.
+ $firstChar = -1;
+ $lastChar = 0;
+ $widths = [];
+ $cid_widths = [];
+
+ foreach ($font['C'] as $num => $d) {
+ if (intval($num) > 0 || $num == '0') {
+ if (!$font['isUnicode']) {
+ // With Unicode, widths array isn't used
+ if ($lastChar > 0 && $num > $lastChar + 1) {
+ for ($i = $lastChar + 1; $i < $num; $i++) {
+ $widths[] = 0;
+ }
+ }
+ }
+
+ $widths[] = $d;
+
+ if ($font['isUnicode']) {
+ $cid_widths[$num] = $d;
+ }
+
+ if ($firstChar == -1) {
+ $firstChar = $num;
+ }
+
+ $lastChar = $num;
+ }
+ }
+
+ // also need to adjust the widths for the differences array
+ if (isset($object['differences'])) {
+ foreach ($object['differences'] as $charNum => $charName) {
+ if ($charNum > $lastChar) {
+ if (!$object['isUnicode']) {
+ // With Unicode, widths array isn't used
+ for ($i = $lastChar + 1; $i <= $charNum; $i++) {
+ $widths[] = 0;
+ }
+ }
+
+ $lastChar = $charNum;
+ }
+
+ if (isset($font['C'][$charName])) {
+ $widths[$charNum - $firstChar] = $font['C'][$charName];
+ if ($font['isUnicode']) {
+ $cid_widths[$charName] = $font['C'][$charName];
+ }
+ }
+ }
+ }
+
+ if ($font['isUnicode']) {
+ $font['CIDWidths'] = $cid_widths;
+ }
+
+ $this->addMessage('selectFont: FirstChar = ' . $firstChar);
+ $this->addMessage('selectFont: LastChar = ' . $lastChar);
+
+ $widthid = -1;
+
+ if (!$font['isUnicode']) {
+ // With Unicode, widths array isn't used
+
+ $this->numObj++;
+ $this->o_contents($this->numObj, 'new', 'raw');
+ $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
+ $widthid = $this->numObj;
+ }
+
+ $missing_width = 500;
+ $stemV = 70;
+
+ if (isset($font['MissingWidth'])) {
+ $missing_width = $font['MissingWidth'];
+ }
+ if (isset($font['StdVW'])) {
+ $stemV = $font['StdVW'];
+ } else {
+ if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
+ $stemV = 120;
+ }
+ }
+
+ // load the pfb file, and put that into an object too.
+ // note that pdf supports only binary format type 1 font files, though there is a
+ // simple utility to convert them from pfa to pfb.
+ if (!$font['isSubsetting']) {
+ $data = file_get_contents($fbfile);
+ } else {
+ $adobeFontName = $this->getFontSubsettingTag($font) . '+' . $adobeFontName;
+ $this->stringSubsets[$fontFileName][] = 32; // Force space if not in yet
+
+ $subset = $this->stringSubsets[$fontFileName];
+ sort($subset);
+
+ // Load font
+ $font_obj = Font::load($fbfile);
+ $font_obj->parse();
+
+ // Define subset
+ $font_obj->setSubset($subset);
+ $font_obj->reduce();
+
+ // Write new font
+ $tmp_name = @tempnam($this->tmp, "cpdf_subset_");
+ $font_obj->open($tmp_name, BinaryStream::modeReadWrite);
+ $font_obj->encode(["OS/2"]);
+ $font_obj->close();
+
+ // Parse the new font to get cid2gid and widths
+ $font_obj = Font::load($tmp_name);
+
+ // Find Unicode char map table
+ $subtable = null;
+ foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
+ if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
+ $subtable = $_subtable;
+ break;
+ }
+ }
+
+ if ($subtable) {
+ $glyphIndexArray = $subtable["glyphIndexArray"];
+ $hmtx = $font_obj->getData("hmtx");
+
+ unset($glyphIndexArray[0xFFFF]);
+
+ $cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
+ $font['CIDWidths'] = [];
+ foreach ($glyphIndexArray as $cid => $gid) {
+ if ($cid >= 0 && $cid < 0xFFFF && $gid) {
+ $cidtogid[$cid * 2] = chr($gid >> 8);
+ $cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
+ }
+
+ $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
+ $font['CIDWidths'][$cid] = $width;
+ }
+
+ $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
+ $font['CIDtoGID_Compressed'] = true;
+
+ $data = file_get_contents($tmp_name);
+ } else {
+ $data = file_get_contents($fbfile);
+ }
+
+ $font_obj->close();
+ unlink($tmp_name);
+ }
+
+ // create the font descriptor
+ $this->numObj++;
+ $fontDescriptorId = $this->numObj;
+
+ $this->numObj++;
+ $pfbid = $this->numObj;
+
+ // determine flags (more than a little flakey, hopefully will not matter much)
+ $flags = 0;
+
+ if ($font['ItalicAngle'] != 0) {
+ $flags += pow(2, 6);
+ }
+
+ if ($font['IsFixedPitch'] === 'true') {
+ $flags += 1;
+ }
+
+ $flags += pow(2, 5); // assume non-sybolic
+ $list = [
+ 'Ascent' => 'Ascender',
+ 'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
+ 'MissingWidth' => 'MissingWidth',
+ 'Descent' => 'Descender',
+ 'FontBBox' => 'FontBBox',
+ 'ItalicAngle' => 'ItalicAngle'
+ ];
+ $fdopt = [
+ 'Flags' => $flags,
+ 'FontName' => $adobeFontName,
+ 'StemV' => $stemV
+ ];
+
+ foreach ($list as $k => $v) {
+ if (isset($font[$v])) {
+ $fdopt[$k] = $font[$v];
+ }
+ }
+
+ if ($isPfbFont) {
+ $fdopt['FontFile'] = $pfbid;
+ } elseif ($isTtfFont) {
+ $fdopt['FontFile2'] = $pfbid;
+ }
+
+ $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
+
+ // embed the font program
+ $this->o_contents($this->numObj, 'new');
+ $this->objects[$pfbid]['c'] .= $data;
+
+ // determine the cruicial lengths within this file
+ if ($isPfbFont) {
+ $l1 = strpos($data, 'eexec') + 6;
+ $l2 = strpos($data, '00000000') - $l1;
+ $l3 = mb_strlen($data, '8bit') - $l2 - $l1;
+ $this->o_contents(
+ $this->numObj,
+ 'add',
+ ['Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3]
+ );
+ } elseif ($isTtfFont) {
+ $l1 = mb_strlen($data, '8bit');
+ $this->o_contents($this->numObj, 'add', ['Length1' => $l1]);
+ }
+
+ // tell the font object about all this new stuff
+ $options = [
+ 'BaseFont' => $adobeFontName,
+ 'MissingWidth' => $missing_width,
+ 'Widths' => $widthid,
+ 'FirstChar' => $firstChar,
+ 'LastChar' => $lastChar,
+ 'FontDescriptor' => $fontDescriptorId
+ ];
+
+ if ($isTtfFont) {
+ $options['SubType'] = 'TrueType';
+ }
+
+ $this->addMessage("adding extra info to font.($fontObjId)");
+
+ foreach ($options as $fk => $fv) {
+ $this->addMessage("$fk : $fv");
+ }
+ }
+
+ return $options;
+ }
+
+ /**
+ * A toUnicode section, needed for unicode fonts
+ *
+ * @param $id
+ * @param $action
+ * @return null|string
+ */
+ protected function o_toUnicode($id, $action)
+ {
+ switch ($action) {
+ case 'new':
+ $this->objects[$id] = [
+ 't' => 'toUnicode'
+ ];
+ break;
+ case 'add':
+ break;
+ case 'out':
+ $ordering = 'UCS';
+ $registry = 'Adobe';
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $ordering = $this->ARC4($ordering);
+ $registry = $this->filterText($this->ARC4($registry), false, false);
+ }
+
+ $stream = <<> def
+/CMapName /Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<0000>
+endcodespacerange
+1 beginbfrange
+<0000> <0000>
+endbfrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+EOT;
+
+ $res = "\n$id 0 obj\n";
+ $res .= "<>\n";
+ $res .= "stream\n" . $stream . "\nendstream" . "\nendobj";
+
+ return $res;
+ }
+
+ return null;
}
/**
* a font descriptor, needed for including additional fonts
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_fontDescriptor($id, $action, $options = '')
{
@@ -896,7 +1396,7 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
+ $this->objects[$id] = ['t' => 'fontDescriptor', 'info' => $options];
break;
case 'out':
@@ -941,10 +1441,17 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* the font encoding
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_fontEncoding($id, $action, $options = '')
{
@@ -955,7 +1462,7 @@ EOT;
switch ($action) {
case 'new':
// the options array should contain 'differences' and maybe 'encoding'
- $this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
+ $this->objects[$id] = ['t' => 'fontEncoding', 'info' => $options];
break;
case 'out':
@@ -987,10 +1494,17 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* a descendent cid font, needed for unicode fonts
+ *
+ * @param $id
+ * @param $action
+ * @param string|array $options
+ * @return null|string
*/
protected function o_fontDescendentCID($id, $action, $options = '')
{
@@ -1000,17 +1514,12 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
+ $this->objects[$id] = ['t' => 'fontDescendentCID', 'info' => $options];
// we need a CID system info section
$cidSystemInfoId = ++$this->numObj;
- $this->o_contents($cidSystemInfoId, 'new', 'raw');
+ $this->o_cidSystemInfo($cidSystemInfoId, 'new');
$this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
- $res = "<>";
- $this->objects[$cidSystemInfoId]['c'] = $res;
// and a CID to GID map
$cidToGidMapId = ++$this->numObj;
@@ -1046,13 +1555,13 @@ EOT;
$res .= "/Subtype /CIDFontType2\n";
$res .= "/BaseFont /" . $o['info']['name'] . "\n";
$res .= "/CIDSystemInfo " . $o['info']['cidSystemInfo'] . " 0 R\n";
-// if (isset($o['info']['FirstChar'])) {
-// $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
-// }
+ // if (isset($o['info']['FirstChar'])) {
+ // $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
+ // }
-// if (isset($o['info']['LastChar'])) {
-// $res.= "/LastChar ".$o['info']['LastChar']."\n";
-// }
+ // if (isset($o['info']['LastChar'])) {
+ // $res.= "/LastChar ".$o['info']['LastChar']."\n";
+ // }
if (isset($o['info']['FontDescriptor'])) {
$res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
}
@@ -1076,10 +1585,60 @@ EOT;
return $res;
}
+
+ return null;
+ }
+
+ /**
+ * CID system info section, needed for unicode fonts
+ *
+ * @param $id
+ * @param $action
+ * @return null|string
+ */
+ protected function o_cidSystemInfo($id, $action)
+ {
+ switch ($action) {
+ case 'new':
+ $this->objects[$id] = [
+ 't' => 'cidSystemInfo'
+ ];
+ break;
+ case 'add':
+ break;
+ case 'out':
+ $ordering = 'UCS';
+ $registry = 'Adobe';
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $ordering = $this->ARC4($ordering);
+ $registry = $this->ARC4($registry);
+ }
+
+
+ $res = "\n$id 0 obj\n";
+
+ $res .= '<>";
+
+ $res .= "\nendobj";
+
+ return $res;
+ }
+
+ return null;
}
/**
* a font glyph to character map, needed for unicode fonts
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_fontGIDtoCIDMap($id, $action, $options = '')
{
@@ -1089,7 +1648,7 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
+ $this->objects[$id] = ['t' => 'fontGIDtoCIDMap', 'info' => $options];
break;
case 'out':
@@ -1114,6 +1673,11 @@ EOT;
$res .= "\n/Filter /FlateDecode";
}
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $tmp = $this->ARC4($tmp);
+ }
+
$res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream";
}
@@ -1121,10 +1685,17 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* the document procset, solves some problems with printing to old PS printers
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_procset($id, $action, $options = '')
{
@@ -1134,14 +1705,14 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
+ $this->objects[$id] = ['t' => 'procset', 'info' => ['PDF' => 1, 'Text' => 1]];
$this->o_pages($this->currentNode, 'procset', $id);
$this->procsetObjectId = $id;
break;
case 'add':
// this is to add new items to the procset list, despite the fact that this is considered
- // obselete, the items are required for printing to some postscript printers
+ // obsolete, the items are required for printing to some postscript printers
switch ($options) {
case 'ImageB':
case 'ImageC':
@@ -1160,28 +1731,31 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* define the document information
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_info($id, $action, $options = '')
{
- if ($action !== 'new') {
- $o = &$this->objects[$id];
- }
-
switch ($action) {
case 'new':
$this->infoObject = $id;
$date = 'D:' . @date('Ymd');
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'info',
- 'info' => array(
- 'Creator' => 'R and OS php pdf writer, http://www.ros.co.nz',
+ 'info' => [
+ 'Producer' => 'CPDF (dompdf)',
'CreationDate' => $date
- )
- );
+ ]
+ ];
break;
case 'Title':
case 'Author':
@@ -1192,26 +1766,30 @@ EOT;
case 'CreationDate':
case 'ModDate':
case 'Trapped':
- $o['info'][$action] = $options;
+ $this->objects[$id]['info'][$action] = $options;
break;
case 'out':
- if ($this->encrypted) {
+ $encrypted = $this->encrypted;
+ if ($encrypted) {
$this->encryptInit($id);
}
$res = "\n$id 0 obj\n<<\n";
+ $o = &$this->objects[$id];
foreach ($o['info'] as $k => $v) {
$res .= "/$k (";
- if ($this->encrypted) {
- $v = $this->ARC4($v);
- } // dates must be outputted as-is, without Unicode transformations
- elseif (!in_array($k, array('CreationDate', 'ModDate'))) {
- $v = $this->filterText($v);
+ // dates must be outputted as-is, without Unicode transformations
+ if ($k !== 'CreationDate' && $k !== 'ModDate') {
+ $v = $this->utf8toUtf16BE($v);
}
- $res .= $v;
+ if ($encrypted) {
+ $v = $this->ARC4($v);
+ }
+
+ $res .= $this->filterText($v, false, false);
$res .= ")\n";
}
@@ -1219,10 +1797,17 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* an action object, used to link to URLS initially
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_action($id, $action, $options = '')
{
@@ -1233,10 +1818,10 @@ EOT;
switch ($action) {
case 'new':
if (is_array($options)) {
- $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
+ $this->objects[$id] = ['t' => 'action', 'info' => $options, 'type' => $options['type']];
} else {
// then assume a URI action
- $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
+ $this->objects[$id] = ['t' => 'action', 'info' => $options, 'type' => 'URI'];
}
break;
@@ -1259,9 +1844,9 @@ EOT;
case 'URI':
$res .= "\n/S /URI\n/URI (";
if ($this->encrypted) {
- $res .= $this->filterText($this->ARC4($o['info']), true, false);
+ $res .= $this->filterText($this->ARC4($o['info']), false, false);
} else {
- $res .= $this->filterText($o['info'], true, false);
+ $res .= $this->filterText($o['info'], false, false);
}
$res .= ")";
@@ -1272,11 +1857,18 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* an annotation object, this will add an annotation to the current page.
* initially will support just link annotations
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_annotation($id, $action, $options = '')
{
@@ -1293,7 +1885,7 @@ EOT;
// and add the action object which is going to be required
switch ($options['type']) {
case 'link':
- $this->objects[$id] = array('t' => 'annotation', 'info' => $options);
+ $this->objects[$id] = ['t' => 'annotation', 'info' => $options];
$this->numObj++;
$this->o_action($this->numObj, 'new', $options['url']);
$this->objects[$id]['info']['actionId'] = $this->numObj;
@@ -1302,9 +1894,9 @@ EOT;
case 'ilink':
// this is to a named internal link
$label = $options['label'];
- $this->objects[$id] = array('t' => 'annotation', 'info' => $options);
+ $this->objects[$id] = ['t' => 'annotation', 'info' => $options];
$this->numObj++;
- $this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
+ $this->o_action($this->numObj, 'new', ['type' => 'ilink', 'label' => $label]);
$this->objects[$id]['info']['actionId'] = $this->numObj;
break;
}
@@ -1332,10 +1924,17 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* a page object, it also creates a contents object to hold its contents
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_page($id, $action, $options = '')
{
@@ -1346,13 +1945,14 @@ EOT;
switch ($action) {
case 'new':
$this->numPages++;
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'page',
- 'info' => array(
+ 'info' => [
'parent' => $this->currentNode,
- 'pageNum' => $this->numPages
- )
- );
+ 'pageNum' => $this->numPages,
+ 'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox']
+ ]
+ ];
if (is_array($options)) {
// then this must be a page insertion, array should contain 'rid','pos'=[before|after]
@@ -1367,7 +1967,7 @@ EOT;
$this->numObj++;
$this->o_contents($this->numObj, 'new', $id);
$this->currentContents = $this->numObj;
- $this->objects[$id]['info']['contents'] = array();
+ $this->objects[$id]['info']['contents'] = [];
$this->objects[$id]['info']['contents'][] = $this->numObj;
$match = ($this->numPages % 2 ? 'odd' : 'even');
@@ -1385,7 +1985,7 @@ EOT;
case 'annot':
// add an annotation to this page
if (!isset($o['info']['annot'])) {
- $o['info']['annot'] = array();
+ $o['info']['annot'] = [];
}
// $options should contain the id of the annotation dictionary
@@ -1394,6 +1994,16 @@ EOT;
case 'out':
$res = "\n$id 0 obj\n<< /Type /Page";
+ if (isset($o['info']['mediaBox'])) {
+ $tmp = $o['info']['mediaBox'];
+ $res .= "\n/MediaBox [" . sprintf(
+ '%.3F %.3F %.3F %.3F',
+ $tmp[0],
+ $tmp[1],
+ $tmp[2],
+ $tmp[3]
+ ) . ']';
+ }
$res .= "\n/Parent " . $o['info']['parent'] . " 0 R";
if (isset($o['info']['annot'])) {
@@ -1425,10 +2035,17 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* the contents objects hold all of the content which appears on pages
+ *
+ * @param $id
+ * @param $action
+ * @param string|array $options
+ * @return null|string
*/
protected function o_contents($id, $action, $options = '')
{
@@ -1438,7 +2055,7 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
+ $this->objects[$id] = ['t' => 'contents', 'c' => '', 'info' => []];
if (mb_strlen($options, '8bit') && intval($options)) {
// then this contents is the primary for a page
$this->objects[$id]['onPage'] = $options;
@@ -1451,7 +2068,7 @@ EOT;
break;
case 'add':
- // add more options to the decleration
+ // add more options to the declaration
foreach ($options as $k => $v) {
$o['info'][$k] = $v;
}
@@ -1486,25 +2103,29 @@ EOT;
return $res;
}
+
+ return null;
}
+ /**
+ * @param $id
+ * @param $action
+ * @return string|null
+ */
protected function o_embedjs($id, $action)
{
- if ($action !== 'new') {
- $o = &$this->objects[$id];
- }
-
switch ($action) {
case 'new':
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'embedjs',
- 'info' => array(
+ 'info' => [
'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]'
- )
- );
+ ]
+ ];
break;
case 'out':
+ $o = &$this->objects[$id];
$res = "\n$id 0 obj\n<< ";
foreach ($o['info'] as $k => $v) {
$res .= "\n/$k $v";
@@ -1513,27 +2134,33 @@ EOT;
return $res;
}
+
+ return null;
}
+ /**
+ * @param $id
+ * @param $action
+ * @param string $code
+ * @return null|string
+ */
protected function o_javascript($id, $action, $code = '')
{
- if ($action !== 'new') {
- $o = &$this->objects[$id];
- }
-
switch ($action) {
case 'new':
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'javascript',
- 'info' => array(
+ 'info' => [
'S' => '/JavaScript',
- 'JS' => '(' . $this->filterText($code) . ')',
- )
- );
+ 'JS' => '(' . $this->filterText($code, true, false) . ')',
+ ]
+ ];
break;
case 'out':
+ $o = &$this->objects[$id];
$res = "\n$id 0 obj\n<< ";
+
foreach ($o['info'] as $k => $v) {
$res .= "\n/$k $v";
}
@@ -1541,21 +2168,24 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* an image object, will be an XObject in the document, includes description and data
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_image($id, $action, $options = '')
{
- if ($action !== 'new') {
- $o = &$this->objects[$id];
- }
-
switch ($action) {
case 'new':
// make the new object
- $this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
+ $this->objects[$id] = ['t' => 'image', 'data' => &$options['data'], 'info' => []];
$info =& $this->objects[$id]['info'];
@@ -1574,10 +2204,10 @@ EOT;
}
switch ($options['channels']) {
- case 1:
+ case 1:
$info['ColorSpace'] = '/DeviceGray';
break;
- case 4:
+ case 4:
$info['ColorSpace'] = '/DeviceCMYK';
break;
default:
@@ -1656,13 +2286,14 @@ EOT;
// assign it a place in the named resource dictionary as an external object, according to
// the label passed in with it.
- $this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
+ $this->o_pages($this->currentNode, 'xObject', ['label' => $options['label'], 'objNum' => $id]);
// also make sure that we have the right procset object for it.
$this->o_procset($this->procsetObjectId, 'add', 'ImageC');
break;
case 'out':
+ $o = &$this->objects[$id];
$tmp = &$o['data'];
$res = "\n$id 0 obj\n<<";
@@ -1679,14 +2310,21 @@ EOT;
return $res;
}
+
+ return null;
}
/**
* graphics state object
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
*/
protected function o_extGState($id, $action, $options = "")
{
- static $valid_params = array(
+ static $valid_params = [
"LW",
"LC",
"LC",
@@ -1713,22 +2351,19 @@ EOT;
"ca",
"AIS",
"TK"
- );
-
- if ($action !== "new") {
- $o = &$this->objects[$id];
- }
+ ];
switch ($action) {
case "new":
- $this->objects[$id] = array('t' => 'extGState', 'info' => $options);
+ $this->objects[$id] = ['t' => 'extGState', 'info' => $options];
// Tell the pages about the new resource
$this->numStates++;
- $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
+ $this->o_pages($this->currentNode, 'extGState', ["objNum" => $id, "stateNum" => $this->numStates]);
break;
case "out":
+ $o = &$this->objects[$id];
$res = "\n$id 0 obj\n<< /Type /ExtGState\n";
foreach ($o["info"] as $k => $v) {
@@ -1742,49 +2377,411 @@ EOT;
return $res;
}
+
+ return null;
+ }
+
+ /**
+ * @param integer $id
+ * @param string $action
+ * @param mixed $options
+ * @return string
+ */
+ protected function o_xobject($id, $action, $options = '')
+ {
+ switch ($action) {
+ case 'new':
+ $this->objects[$id] = ['t' => 'xobject', 'info' => $options, 'c' => ''];
+ break;
+
+ case 'procset':
+ $this->objects[$id]['procset'] = $options;
+ break;
+
+ case 'font':
+ $this->objects[$id]['fonts'][$options['fontNum']] = [
+ 'objNum' => $options['objNum'],
+ 'fontNum' => $options['fontNum']
+ ];
+ break;
+
+ case 'xObject':
+ $this->objects[$id]['xObjects'][] = ['objNum' => $options['objNum'], 'label' => $options['label']];
+ break;
+
+ case 'out':
+ $o = &$this->objects[$id];
+ $res = "\n$id 0 obj\n<< /Type /XObject\n";
+
+ foreach ($o["info"] as $k => $v) {
+ switch ($k) {
+ case 'Subtype':
+ $res .= "/Subtype /$v\n";
+ break;
+ case 'bbox':
+ $res .= "/BBox [";
+ foreach ($v as $value) {
+ $res .= sprintf("%.4F ", $value);
+ }
+ $res .= "]\n";
+ break;
+ default:
+ $res .= "/$k $v\n";
+ break;
+ }
+ }
+ $res .= "/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]\n";
+
+ $res .= "/Resources <<";
+ if (isset($o['procset'])) {
+ $res .= "\n/ProcSet " . $o['procset'] . " 0 R";
+ } else {
+ $res .= "\n/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]";
+ }
+ if (isset($o['fonts']) && count($o['fonts'])) {
+ $res .= "\n/Font << ";
+ foreach ($o['fonts'] as $finfo) {
+ $res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
+ }
+ $res .= "\n>>";
+ }
+ if (isset($o['xObjects']) && count($o['xObjects'])) {
+ $res .= "\n/XObject << ";
+ foreach ($o['xObjects'] as $finfo) {
+ $res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
+ }
+ $res .= "\n>>";
+ }
+ $res .= "\n>>\n";
+
+ $tmp = $o["c"];
+ if ($this->compressionReady && $this->options['compression']) {
+ // then implement ZLIB based compression on this content stream
+ $res .= " /Filter /FlateDecode\n";
+ $tmp = gzcompress($tmp, 6);
+ }
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $tmp = $this->ARC4($tmp);
+ }
+
+ $res .= "/Length " . mb_strlen($tmp, '8bit') . " >>\n";
+ $res .= "stream\n" . $tmp . "\nendstream" . "\nendobj";
+
+ return $res;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
+ */
+ protected function o_acroform($id, $action, $options = '')
+ {
+ switch ($action) {
+ case "new":
+ $this->o_catalog($this->catalogId, 'acroform', $id);
+ $this->objects[$id] = array('t' => 'acroform', 'info' => $options);
+ break;
+
+ case 'addfield':
+ $this->objects[$id]['info']['Fields'][] = $options;
+ break;
+
+ case 'font':
+ $this->objects[$id]['fonts'][$options['fontNum']] = [
+ 'objNum' => $options['objNum'],
+ 'fontNum' => $options['fontNum']
+ ];
+ break;
+
+ case "out":
+ $o = &$this->objects[$id];
+ $res = "\n$id 0 obj\n<<";
+
+ foreach ($o["info"] as $k => $v) {
+ switch ($k) {
+ case 'Fields':
+ $res .= " /Fields [";
+ foreach ($v as $i) {
+ $res .= "$i 0 R ";
+ }
+ $res .= "]\n";
+ break;
+ default:
+ $res .= "/$k $v\n";
+ }
+ }
+
+ $res .= "/DR <<\n";
+ if (isset($o['fonts']) && count($o['fonts'])) {
+ $res .= "/Font << \n";
+ foreach ($o['fonts'] as $finfo) {
+ $res .= "/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R\n";
+ }
+ $res .= ">>\n";
+ }
+ $res .= ">>\n";
+
+ $res .= ">>\nendobj";
+
+ return $res;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $id
+ * @param $action
+ * @param mixed $options
+ * @return null|string
+ */
+ protected function o_field($id, $action, $options = '')
+ {
+ switch ($action) {
+ case "new":
+ $this->o_page($options['pageid'], 'annot', $id);
+ $this->o_acroform($this->acroFormId, 'addfield', $id);
+ $this->objects[$id] = ['t' => 'field', 'info' => $options];
+ break;
+
+ case 'set':
+ $this->objects[$id]['info'] = array_merge($this->objects[$id]['info'], $options);
+ break;
+
+ case "out":
+ $o = &$this->objects[$id];
+ $res = "\n$id 0 obj\n<< /Type /Annot /Subtype /Widget \n";
+
+ $encrypted = $this->encrypted;
+ if ($encrypted) {
+ $this->encryptInit($id);
+ }
+
+ foreach ($o["info"] as $k => $v) {
+ switch ($k) {
+ case 'pageid':
+ $res .= "/P $v 0 R\n";
+ break;
+ case 'value':
+ if ($encrypted) {
+ $v = $this->filterText($this->ARC4($v), false, false);
+ }
+ $res .= "/V ($v)\n";
+ break;
+ case 'refvalue':
+ $res .= "/V $v 0 R\n";
+ break;
+ case 'da':
+ if ($encrypted) {
+ $v = $this->filterText($this->ARC4($v), false, false);
+ }
+ $res .= "/DA ($v)\n";
+ break;
+ case 'options':
+ $res .= "/Opt [\n";
+ foreach ($v as $opt) {
+ if ($encrypted) {
+ $opt = $this->filterText($this->ARC4($opt), false, false);
+ }
+ $res .= "($opt)\n";
+ }
+ $res .= "]\n";
+ break;
+ case 'rect':
+ $res .= "/Rect [";
+ foreach ($v as $value) {
+ $res .= sprintf("%.4F ", $value);
+ }
+ $res .= "]\n";
+ break;
+ case 'appearance':
+ $res .= "/AP << ";
+ foreach ($v as $a => $ref) {
+ $res .= "/$a $ref 0 R ";
+ }
+ $res .= ">>\n";
+ break;
+ case 'T':
+ if ($encrypted) {
+ $v = $this->filterText($this->ARC4($v), false, false);
+ }
+ $res .= "/T ($v)\n";
+ break;
+ default:
+ $res .= "/$k $v\n";
+ }
+
+ }
+
+ $res .= ">>\nendobj";
+
+ return $res;
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
+ */
+ protected function o_sig($id, $action, $options = '')
+ {
+ $sign_maxlen = $this->signatureMaxLen;
+
+ switch ($action) {
+ case "new":
+ $this->objects[$id] = array('t' => 'sig', 'info' => $options);
+ $this->byteRange[$id] = ['t' => 'sig'];
+ break;
+
+ case 'byterange':
+ $o = &$this->objects[$id];
+ $content =& $options['content'];
+ $content_len = strlen($content);
+ $pos = strpos($content, sprintf("/ByteRange [ %'.010d", $id));
+ $len = strlen('/ByteRange [ ********** ********** ********** ********** ]');
+ $rangeStartPos = $pos + $len + 1 + 10; // before '<'
+ $content = substr_replace($content, str_pad(sprintf('/ByteRange [ 0 %u %u %u ]', $rangeStartPos, $rangeStartPos + $sign_maxlen + 2, $content_len - 2 - $sign_maxlen - $rangeStartPos), $len, ' ', STR_PAD_RIGHT), $pos, $len);
+
+ $fuid = uniqid();
+ $tmpInput = $this->tmp . "/pkcs7.tmp." . $fuid . '.in';
+ $tmpOutput = $this->tmp . "/pkcs7.tmp." . $fuid . '.out';
+
+ if (file_put_contents($tmpInput, substr($content, 0, $rangeStartPos)) === false) {
+ throw new \Exception("Unable to write temporary file for signing.");
+ }
+ if (file_put_contents($tmpInput, substr($content, $rangeStartPos + 2 + $sign_maxlen),
+ FILE_APPEND) === false) {
+ throw new \Exception("Unable to write temporary file for signing.");
+ }
+
+ if (openssl_pkcs7_sign($tmpInput, $tmpOutput,
+ $o['info']['SignCert'],
+ array($o['info']['PrivKey'], $o['info']['Password']),
+ array(), PKCS7_BINARY | PKCS7_DETACHED) === false) {
+ throw new \Exception("Failed to prepare signature.");
+ }
+
+ $signature = file_get_contents($tmpOutput);
+
+ unlink($tmpInput);
+ unlink($tmpOutput);
+
+ $sign = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
+ list($head, $signature) = explode("\n\n", $sign);
+
+ $signature = base64_decode(trim($signature));
+
+ $signature = current(unpack('H*', $signature));
+ $signature = str_pad($signature, $sign_maxlen, '0');
+ $siglen = strlen($signature);
+ if (strlen($signature) > $sign_maxlen) {
+ throw new \Exception("Signature length ($siglen) exceeds the $sign_maxlen limit.");
+ }
+
+ $content = substr_replace($content, $signature, $rangeStartPos + 1, $sign_maxlen);
+ break;
+
+ case "out":
+ $res = "\n$id 0 obj\n<<\n";
+
+ $encrypted = $this->encrypted;
+ if ($encrypted) {
+ $this->encryptInit($id);
+ }
+
+ $res .= "/ByteRange " .sprintf("[ %'.010d ********** ********** ********** ]\n", $id);
+ $res .= "/Contents <" . str_pad('', $sign_maxlen, '0') . ">\n";
+ $res .= "/Filter/Adobe.PPKLite\n"; //PPKMS \n";
+ $res .= "/Type/Sig/SubFilter/adbe.pkcs7.detached \n";
+
+ $date = "D:" . substr_replace(date('YmdHisO'), '\'', -2, 0) . '\'';
+ if ($encrypted) {
+ $date = $this->ARC4($date);
+ }
+
+ $res .= "/M ($date)\n";
+ $res .= "/Prop_Build << /App << /Name /DomPDF >> /Filter << /Name /Adobe.PPKLite >> >>\n";
+
+ $o = &$this->objects[$id];
+ foreach ($o['info'] as $k => $v) {
+ switch ($k) {
+ case 'Name':
+ case 'Location':
+ case 'Reason':
+ case 'ContactInfo':
+ if ($v !== null && $v !== '') {
+ $res .= "/$k (" .
+ ($encrypted ? $this->filterText($this->ARC4($v), false, false) : $v) . ") \n";
+ }
+ break;
+ }
+ }
+ $res .= ">>\nendobj";
+
+ return $res;
+ }
+
+ return null;
}
/**
* encryption object.
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return string|null
*/
protected function o_encryption($id, $action, $options = '')
{
- if ($action !== 'new') {
- $o = &$this->objects[$id];
- }
-
switch ($action) {
case 'new':
// make the new object
- $this->objects[$id] = array('t' => 'encryption', 'info' => $options);
+ $this->objects[$id] = ['t' => 'encryption', 'info' => $options];
$this->arc4_objnum = $id;
+ break;
- // figure out the additional paramaters required
+ case 'keys':
+ // figure out the additional parameters required
$pad = chr(0x28) . chr(0xBF) . chr(0x4E) . chr(0x5E) . chr(0x4E) . chr(0x75) . chr(0x8A) . chr(0x41)
. chr(0x64) . chr(0x00) . chr(0x4E) . chr(0x56) . chr(0xFF) . chr(0xFA) . chr(0x01) . chr(0x08)
. chr(0x2E) . chr(0x2E) . chr(0x00) . chr(0xB6) . chr(0xD0) . chr(0x68) . chr(0x3E) . chr(0x80)
. chr(0x2F) . chr(0x0C) . chr(0xA9) . chr(0xFE) . chr(0x64) . chr(0x53) . chr(0x69) . chr(0x7A);
- $len = mb_strlen($options['owner'], '8bit');
+ $info = $this->objects[$id]['info'];
+
+ $len = mb_strlen($info['owner'], '8bit');
if ($len > 32) {
- $owner = substr($options['owner'], 0, 32);
+ $owner = substr($info['owner'], 0, 32);
} else {
if ($len < 32) {
- $owner = $options['owner'] . substr($pad, 0, 32 - $len);
+ $owner = $info['owner'] . substr($pad, 0, 32 - $len);
} else {
- $owner = $options['owner'];
+ $owner = $info['owner'];
}
}
- $len = mb_strlen($options['user'], '8bit');
+ $len = mb_strlen($info['user'], '8bit');
if ($len > 32) {
- $user = substr($options['user'], 0, 32);
+ $user = substr($info['user'], 0, 32);
} else {
if ($len < 32) {
- $user = $options['user'] . substr($pad, 0, 32 - $len);
+ $user = $info['user'] . substr($pad, 0, 32 - $len);
} else {
- $user = $options['user'];
+ $user = $info['user'];
}
}
@@ -1796,7 +2793,7 @@ EOT;
// now make the u value, phew.
$tmp = $this->md5_16(
- $user . $ovalue . chr($options['p']) . chr(255) . chr(255) . chr(255) . $this->fileIdentifier
+ $user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier)
);
$ukey = substr($tmp, 0, 5);
@@ -1805,17 +2802,18 @@ EOT;
$this->encrypted = true;
$uvalue = $this->ARC4($pad);
$this->objects[$id]['info']['U'] = $uvalue;
- $this->encryptionKey = $ukey;
// initialize the arc4 array
break;
case 'out':
+ $o = &$this->objects[$id];
+
$res = "\n$id 0 obj\n<<";
$res .= "\n/Filter /Standard";
$res .= "\n/V 1";
$res .= "\n/R 2";
- $res .= "\n/O (" . $this->filterText($o['info']['O'], true, false) . ')';
- $res .= "\n/U (" . $this->filterText($o['info']['U'], true, false) . ')';
+ $res .= "\n/O (" . $this->filterText($o['info']['O'], false, false) . ')';
+ $res .= "\n/U (" . $this->filterText($o['info']['U'], false, false) . ')';
// and the p-value needs to be converted to account for the twos-complement approach
$o['info']['p'] = (($o['info']['p'] ^ 255) + 1) * -1;
$res .= "\n/P " . ($o['info']['p']);
@@ -1823,6 +2821,158 @@ EOT;
return $res;
}
+
+ return null;
+ }
+
+ protected function o_indirect_references($id, $action, $options = null)
+ {
+ switch ($action) {
+ case 'new':
+ case 'add':
+ if ($id === 0) {
+ $id = ++$this->numObj;
+ $this->o_catalog($this->catalogId, 'names', $id);
+ $this->objects[$id] = ['t' => 'indirect_references', 'info' => $options];
+ $this->indirectReferenceId = $id;
+ } else {
+ $this->objects[$id]['info'] = array_merge($this->objects[$id]['info'], $options);
+ }
+ break;
+ case 'out':
+ $res = "\n$id 0 obj << ";
+
+ foreach ($this->objects[$id]['info'] as $referenceObjName => $referenceObjId) {
+ $res .= "/$referenceObjName $referenceObjId 0 R ";
+ }
+
+ $res .= ">> endobj";
+ return $res;
+ }
+
+ return null;
+ }
+
+ protected function o_names($id, $action, $options = null)
+ {
+ switch ($action) {
+ case 'new':
+ case 'add':
+ if ($id === 0) {
+ $id = ++$this->numObj;
+ $this->objects[$id] = ['t' => 'names', 'info' => [$options]];
+ $this->o_indirect_references($this->indirectReferenceId, 'add', ['EmbeddedFiles' => $id]);
+ $this->embeddedFilesId = $id;
+ } else {
+ $this->objects[$id]['info'][] = $options;
+ }
+ break;
+ case 'out':
+ $info = &$this->objects[$id]['info'];
+ $res = '';
+ if (count($info) > 0) {
+ $res = "\n$id 0 obj << /Names [ ";
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ }
+
+ foreach ($info as $entry) {
+ if ($this->encrypted) {
+ $filename = $this->ARC4($entry['filename']);
+ } else {
+ $filename = $entry['filename'];
+ }
+
+ $res .= "($filename) " . $entry['dict_reference'] . " 0 R ";
+ }
+
+ $res .= "] >> endobj";
+ }
+ return $res;
+ }
+
+ return null;
+ }
+
+ protected function o_embedded_file_dictionary($id, $action, $options = null)
+ {
+ switch ($action) {
+ case 'new':
+ $embeddedFileId = ++$this->numObj;
+ $options['embedded_reference'] = $embeddedFileId;
+ $this->objects[$id] = ['t' => 'embedded_file_dictionary', 'info' => $options];
+ $this->o_embedded_file($embeddedFileId, 'new', $options);
+ $options['dict_reference'] = $id;
+ $this->o_names($this->embeddedFilesId, 'add', $options);
+ break;
+ case 'out':
+ $info = &$this->objects[$id]['info'];
+ $filename = $this->utf8toUtf16BE($info['filename']);
+ $description = $this->utf8toUtf16BE($info['description']);
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $filename = $this->ARC4($filename);
+ $description = $this->ARC4($description);
+ }
+
+ $filename = $this->filterText($filename, false, false);
+ $description = $this->filterText($description, false, false);
+
+ $res = "\n$id 0 obj <>";
+ $res .= " /F ($filename) /UF ($filename) /Desc ($description)";
+ $res .= " >> endobj";
+ return $res;
+ }
+
+ return null;
+ }
+
+ protected function o_embedded_file($id, $action, $options = null): ?string
+ {
+ switch ($action) {
+ case 'new':
+ $this->objects[$id] = ['t' => 'embedded_file', 'info' => $options];
+ break;
+ case 'out':
+ $info = &$this->objects[$id]['info'];
+
+ if ($this->compressionReady) {
+ $filepath = $info['filepath'];
+ $checksum = md5_file($filepath);
+ $f = fopen($filepath, "rb");
+
+ $file_content_compressed = '';
+ $deflateContext = deflate_init(ZLIB_ENCODING_DEFLATE, ['level' => 6]);
+ while (($block = fread($f, 8192))) {
+ $file_content_compressed .= deflate_add($deflateContext, $block, ZLIB_NO_FLUSH);
+ }
+ $file_content_compressed .= deflate_add($deflateContext, '', ZLIB_FINISH);
+ $file_size_uncompressed = ftell($f);
+ fclose($f);
+ } else {
+ $file_content = file_get_contents($info['filepath']);
+ $file_size_uncompressed = mb_strlen($file_content, '8bit');
+ $checksum = md5($file_content);
+ }
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $checksum = $this->ARC4($checksum);
+ $file_content_compressed = $this->ARC4($file_content_compressed);
+ }
+ $file_size_compressed = mb_strlen($file_content_compressed, '8bit');
+
+ $res = "\n$id 0 obj <>" .
+ " /Type/EmbeddedFile /Filter/FlateDecode" .
+ " /Length $file_size_compressed >> stream\n$file_content_compressed\nendstream\nendobj";
+
+ return $res;
+ }
+
+ return null;
}
/**
@@ -1832,6 +2982,9 @@ EOT;
/**
* calculate the 16 byte version of the 128 bit md5 digest of the string
+ *
+ * @param $string
+ * @return string
*/
function md5_16($string)
{
@@ -1846,6 +2999,8 @@ EOT;
/**
* initialize the encryption for processing a particular object
+ *
+ * @param $id
*/
function encryptInit($id)
{
@@ -1854,15 +3009,20 @@ EOT;
if (mb_strlen($hex, '8bit') < 6) {
$hex = substr('000000', 0, 6 - mb_strlen($hex, '8bit')) . $hex;
}
- $tmp .= chr(hexdec(substr($hex, 4, 2))) . chr(hexdec(substr($hex, 2, 2))) . chr(
- hexdec(substr($hex, 0, 2))
- ) . chr(0) . chr(0);
+ $tmp .= chr(hexdec(substr($hex, 4, 2)))
+ . chr(hexdec(substr($hex, 2, 2)))
+ . chr(hexdec(substr($hex, 0, 2)))
+ . chr(0)
+ . chr(0)
+ ;
$key = $this->md5_16($tmp);
$this->ARC4_init(substr($key, 0, 10));
}
/**
* initialize the ARC4 encryption
+ *
+ * @param string $key
*/
function ARC4_init($key = '')
{
@@ -1895,6 +3055,9 @@ EOT;
/**
* ARC4 encrypt a text string
+ *
+ * @param $text
+ * @return string
*/
function ARC4($text)
{
@@ -1922,21 +3085,33 @@ EOT;
/**
* add a link in the document to an external URL
+ *
+ * @param $url
+ * @param $x0
+ * @param $y0
+ * @param $x1
+ * @param $y1
*/
function addLink($url, $x0, $y0, $x1, $y1)
{
$this->numObj++;
- $info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
+ $info = ['type' => 'link', 'url' => $url, 'rect' => [$x0, $y0, $x1, $y1]];
$this->o_annotation($this->numObj, 'new', $info);
}
/**
* add a link in the document to an internal destination (ie. within the document)
+ *
+ * @param $label
+ * @param $x0
+ * @param $y0
+ * @param $x1
+ * @param $y1
*/
function addInternalLink($label, $x0, $y0, $x1, $y1)
{
$this->numObj++;
- $info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
+ $info = ['type' => 'ilink', 'label' => $label, 'rect' => [$x0, $y0, $x1, $y1]];
$this->o_annotation($this->numObj, 'new', $info);
}
@@ -1944,12 +3119,16 @@ EOT;
* set the encryption of the document
* can be used to turn it on and/or set the passwords which it will have.
* also the functions that the user will have are set here, such as print, modify, add
+ *
+ * @param string $userPass
+ * @param string $ownerPass
+ * @param array $pc
*/
- function setEncryption($userPass = '', $ownerPass = '', $pc = array())
+ function setEncryption($userPass = '', $ownerPass = '', $pc = [])
{
$p = bindec("11000000");
- $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
+ $options = ['print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32];
foreach ($pc as $k => $v) {
if ($v && isset($options[$k])) {
@@ -1969,7 +3148,7 @@ EOT;
$ownerPass = $userPass;
}
- $this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
+ $this->o_encryption($this->numObj, 'new', ['user' => $userPass, 'owner' => $ownerPass, 'p' => $p]);
}
}
@@ -1982,6 +3161,9 @@ EOT;
/**
* return the pdf stream as a string returned from the function
+ *
+ * @param bool $debug
+ * @return string
*/
function output($debug = false)
{
@@ -1999,24 +3181,37 @@ EOT;
$id = $this->catalogId;
- $this->o_catalog($id, 'javascript', $js_id);
+ $this->o_indirect_references($this->indirectReferenceId, 'add', ['JavaScript' => $js_id]);
+ }
+
+ if ($this->fileIdentifier === '') {
+ $tmp = implode('', $this->objects[$this->infoObject]['info']);
+ $this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand());
}
if ($this->arc4_objnum) {
+ $this->o_encryption($this->arc4_objnum, 'keys');
$this->ARC4_init($this->encryptionKey);
}
$this->checkAllHere();
- $xref = array();
- $content = '%PDF-1.3';
+ $xref = [];
+ $content = '%PDF-' . self::PDF_VERSION;
$pos = mb_strlen($content, '8bit');
+ // pre-process o_font objects before output of all objects
+ foreach ($this->objects as $k => $v) {
+ if ($v['t'] === 'font') {
+ $this->o_font($k, 'add');
+ }
+ }
+
foreach ($this->objects as $k => $v) {
$tmp = 'o_' . $v['t'];
$cont = $this->$tmp($k, 'out');
$content .= $cont;
- $xref[] = $pos;
+ $xref[] = $pos + 1; //+1 to account for \n at the start of each object
$pos += mb_strlen($cont, '8bit');
}
@@ -2026,34 +3221,45 @@ EOT;
$content .= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
}
- $content .= "trailer\n<<\n/Size " . (count($xref) + 1) . "\n/Root 1 0 R\n/Info $this->infoObject 0 R\n";
+ $content .= "trailer\n<<\n" .
+ '/Size ' . (count($xref) + 1) . "\n" .
+ '/Root 1 0 R' . "\n" .
+ '/Info ' . $this->infoObject . " 0 R\n"
+ ;
// if encryption has been applied to this document then add the marker for this dictionary
if ($this->arc4_objnum > 0) {
- $content .= "/Encrypt $this->arc4_objnum 0 R\n";
+ $content .= '/Encrypt ' . $this->arc4_objnum . " 0 R\n";
}
- if (mb_strlen($this->fileIdentifier, '8bit')) {
- $content .= "/ID[<$this->fileIdentifier><$this->fileIdentifier>]\n";
- }
+ $content .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n";
// account for \n added at start of xref table
$pos++;
$content .= ">>\nstartxref\n$pos\n%%EOF\n";
+ if (count($this->byteRange) > 0) {
+ foreach ($this->byteRange as $k => $v) {
+ $tmp = 'o_' . $v['t'];
+ $this->$tmp($k, 'byterange', ['content' => &$content]);
+ }
+ }
+
return $content;
}
/**
- * intialize a new document
+ * initialize a new document
* if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
* this function is called automatically by the constructor function
+ *
+ * @param array $pageSize
*/
- private function newDocument($pageSize = array(0, 0, 612, 792))
+ private function newDocument($pageSize = [0, 0, 612, 792])
{
$this->numObj = 0;
- $this->objects = array();
+ $this->objects = [];
$this->numObj++;
$this->o_catalog($this->numObj, 'new');
@@ -2087,19 +3293,14 @@ EOT;
* note that if a php serialized version does not exist it will try and make one, but will
* require write access to the directory to do it... it is MUCH faster to have these serialized
* files.
+ *
+ * @param $font
*/
private function openFont($font)
{
// assume that $font contains the path and file but not the extension
- $pos = strrpos($font, '/');
-
- if ($pos === false) {
- $dir = './';
- $name = $font;
- } else {
- $dir = substr($font, 0, $pos + 1);
- $name = substr($font, $pos + 1);
- }
+ $name = basename($font);
+ $dir = dirname($font);
$fontcache = $this->fontcache;
if ($fontcache == '') {
@@ -2107,7 +3308,7 @@ EOT;
}
//$name filename without folder and extension of font metrics
- //$dir folder of font metrics
+ //$dir folder of font metrics
//$fontcache folder of runtime created php serialized version of font metrics.
// If this is not given, the same folder as the font metrics will be used.
// Storing and reusing serialized versions improves speed much
@@ -2120,39 +3321,26 @@ EOT;
$metrics_name = "$name.ufm";
}
- $cache_name = "$metrics_name.php";
+ $cache_name = "$metrics_name.json";
$this->addMessage("metrics: $metrics_name, cache: $cache_name");
-
- if (file_exists($fontcache . $cache_name)) {
- $this->addMessage("openFont: php file exists $fontcache$cache_name");
- $this->fonts[$font] = require($fontcache . $cache_name);
-
- if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
- // if the font file is old, then clear it out and prepare for re-creation
- $this->addMessage('openFont: clear out, make way for new version.');
- $this->fonts[$font] = null;
- unset($this->fonts[$font]);
- }
- } else {
- $old_cache_name = "php_$metrics_name";
- if (file_exists($fontcache . $old_cache_name)) {
- $this->addMessage(
- "openFont: php file doesn't exist $fontcache$cache_name, creating it from the old format"
- );
- $old_cache = file_get_contents($fontcache . $old_cache_name);
- file_put_contents($fontcache . $cache_name, 'openFont($font);
+
+ if (file_exists($fontcache . '/' . $cache_name)) {
+ $this->addMessage("openFont: json metrics file exists $fontcache/$cache_name");
+ $cached_font_info = json_decode(file_get_contents($fontcache . '/' . $cache_name), true);
+ if (!isset($cached_font_info['_version_']) || $cached_font_info['_version_'] != $this->fontcacheVersion) {
+ $this->addMessage('openFont: font cache is out of date, regenerating');
+ } else {
+ $this->fonts[$font] = $cached_font_info;
}
}
- if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
+ if (!isset($this->fonts[$font]) && file_exists("$dir/$metrics_name")) {
// then rebuild the php_.afm file from the .afm file
- $this->addMessage("openFont: build php file from $dir$metrics_name");
- $data = array();
+ $this->addMessage("openFont: build php file from $dir/$metrics_name");
+ $data = [];
// 20 => 'space'
- $data['codeToName'] = array();
+ $data['codeToName'] = [];
// Since we're not going to enable Unicode for the core fonts we need to use a font-based
// setting for Unicode support rather than a global setting.
@@ -2163,7 +3351,7 @@ EOT;
$cidtogid = str_pad('', 256 * 256 * 2, "\x00");
}
- $file = file($dir . $metrics_name);
+ $file = file("$dir/$metrics_name");
foreach ($file as $rowA) {
$row = trim($rowA);
@@ -2203,7 +3391,7 @@ EOT;
//C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
case 'C': // Found in AFM files
$bits = explode(';', trim($row));
- $dtmp = array();
+ $dtmp = ['C' => null, 'N' => null, 'WX' => null, 'B' => []];
foreach ($bits as $bit) {
$bits2 = explode(' ', trim($bit));
@@ -2212,7 +3400,7 @@ EOT;
}
if (count($bits2) > 2) {
- $dtmp[$bits2[0]] = array();
+ $dtmp[$bits2[0]] = [];
for ($i = 1; $i < count($bits2); $i++) {
$dtmp[$bits2[0]][] = $bits2[$i];
}
@@ -2228,15 +3416,15 @@ EOT;
$width = floatval($dtmp['WX']);
if ($c >= 0) {
- if ($c != hexdec($n)) {
+ if (!ctype_xdigit($n) || $c != hexdec($n)) {
$data['codeToName'][$c] = $n;
}
$data['C'][$c] = $width;
- } else {
+ } elseif (isset($n)) {
$data['C'][$n] = $width;
}
- if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
+ if (!isset($data['MissingWidth']) && $c === -1 && $n === '.notdef') {
$data['MissingWidth'] = $width;
}
@@ -2249,7 +3437,7 @@ EOT;
}
$bits = explode(';', trim($row));
- $dtmp = array();
+ $dtmp = ['G' => null, 'N' => null, 'U' => null, 'WX' => null];
foreach ($bits as $bit) {
$bits2 = explode(' ', trim($bit));
@@ -2258,7 +3446,7 @@ EOT;
}
if (count($bits2) > 2) {
- $dtmp[$bits2[0]] = array();
+ $dtmp[$bits2[0]] = [];
for ($i = 1; $i < count($bits2); $i++) {
$dtmp[$bits2[0]][] = $bits2[$i];
}
@@ -2281,15 +3469,15 @@ EOT;
$cidtogid[$c * 2 + 1] = chr($glyph & 0xFF);
}
- if ($c != hexdec($n)) {
+ if (!ctype_xdigit($n) || $c != hexdec($n)) {
$data['codeToName'][$c] = $n;
}
$data['C'][$c] = $width;
- } else {
+ } elseif (isset($n)) {
$data['C'][$n] = $width;
}
- if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
+ if (!isset($data['MissingWidth']) && $c === -1 && $n === '.notdef') {
$data['MissingWidth'] = $width;
}
@@ -2298,9 +3486,9 @@ EOT;
case 'KPX':
break; // don't include them as they are not used yet
//KPX Adieresis yacute -40
- $bits = explode(' ', trim($row));
+ /*$bits = explode(' ', trim($row));
$data['KPX'][$bits[1]][$bits[2]] = $bits[3];
- break;
+ break;*/
}
}
}
@@ -2316,8 +3504,8 @@ EOT;
//Because of potential trouble with php safe mode, expect that the folder already exists.
//If not existing, this will hit performance because of missing cached results.
- if (is_dir(substr($fontcache, 0, -1)) && is_writable(substr($fontcache, 0, -1))) {
- file_put_contents($fontcache . $cache_name, 'fonts[$font])) {
$this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
}
-
- //pre_r($this->messages);
}
/**
@@ -2336,9 +3522,19 @@ EOT;
* note that encoding='none' will need to be used for symbolic fonts
* and 'differences' => an array of mappings between numbers 0->255 and character names.
*
+ * @param string $fontName
+ * @param string $encoding
+ * @param bool $set
+ * @param bool $isSubsetting
+ * @return int
+ * @throws FontNotFoundException
*/
- function selectFont($fontName, $encoding = '', $set = true)
+ function selectFont($fontName, $encoding = '', $set = true, $isSubsetting = true)
{
+ if ($fontName === null || $fontName === '') {
+ return $this->currentFontNum;
+ }
+
$ext = substr($fontName, -4);
if ($ext === '.afm' || $ext === '.ufm') {
$fontName = substr($fontName, 0, mb_strlen($fontName) - 4);
@@ -2356,11 +3552,8 @@ EOT;
$font = &$this->fonts[$fontName];
- //$this->numFonts = md5($fontName);
- $pos = strrpos($fontName, '/');
- // $dir = substr($fontName,0,$pos+1);
- $name = substr($fontName, $pos + 1);
- $options = array('name' => $name, 'fontFileName' => $fontName);
+ $name = basename($fontName);
+ $options = ['name' => $name, 'fontFileName' => $fontName, 'isSubsetting' => $isSubsetting];
if (is_array($encoding)) {
// then encoding and differences might be set
@@ -2378,287 +3571,24 @@ EOT;
}
}
- $fontObj = $this->numObj;
$this->o_font($this->numObj, 'new', $options);
+
+ if (file_exists("$fontName.ttf")) {
+ $fileSuffix = 'ttf';
+ } elseif (file_exists("$fontName.TTF")) {
+ $fileSuffix = 'TTF';
+ } elseif (file_exists("$fontName.pfb")) {
+ $fileSuffix = 'pfb';
+ } elseif (file_exists("$fontName.PFB")) {
+ $fileSuffix = 'PFB';
+ } else {
+ $fileSuffix = '';
+ }
+
+ $font['fileSuffix'] = $fileSuffix;
+
$font['fontNum'] = $this->numFonts;
-
- // if this is a '.afm' font, and there is a '.pfa' file to go with it ( as there
- // should be for all non-basic fonts), then load it into an object and put the
- // references into the font object
- $basefile = $fontName;
-
- $fbtype = '';
- if (file_exists("$basefile.pfb")) {
- $fbtype = 'pfb';
- } else {
- if (file_exists("$basefile.ttf")) {
- $fbtype = 'ttf';
- }
- }
-
- $fbfile = "$basefile.$fbtype";
-
- // $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
- // $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
- $this->addMessage('selectFont: checking for - ' . $fbfile);
-
- // OAR - I don't understand this old check
- // if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
- if ($fbtype) {
- $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
- // $fontObj = $this->numObj;
- $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
-
- // find the array of font widths, and put that into an object.
- $firstChar = -1;
- $lastChar = 0;
- $widths = array();
- $cid_widths = array();
-
- foreach ($font['C'] as $num => $d) {
- if (intval($num) > 0 || $num == '0') {
- if (!$font['isUnicode']) {
- // With Unicode, widths array isn't used
- if ($lastChar > 0 && $num > $lastChar + 1) {
- for ($i = $lastChar + 1; $i < $num; $i++) {
- $widths[] = 0;
- }
- }
- }
-
- $widths[] = $d;
-
- if ($font['isUnicode']) {
- $cid_widths[$num] = $d;
- }
-
- if ($firstChar == -1) {
- $firstChar = $num;
- }
-
- $lastChar = $num;
- }
- }
-
- // also need to adjust the widths for the differences array
- if (isset($options['differences'])) {
- foreach ($options['differences'] as $charNum => $charName) {
- if ($charNum > $lastChar) {
- if (!$font['isUnicode']) {
- // With Unicode, widths array isn't used
- for ($i = $lastChar + 1; $i <= $charNum; $i++) {
- $widths[] = 0;
- }
- }
-
- $lastChar = $charNum;
- }
-
- if (isset($font['C'][$charName])) {
- $widths[$charNum - $firstChar] = $font['C'][$charName];
- if ($font['isUnicode']) {
- $cid_widths[$charName] = $font['C'][$charName];
- }
- }
- }
- }
-
- if ($font['isUnicode']) {
- $font['CIDWidths'] = $cid_widths;
- }
-
- $this->addMessage('selectFont: FirstChar = ' . $firstChar);
- $this->addMessage('selectFont: LastChar = ' . $lastChar);
-
- $widthid = -1;
-
- if (!$font['isUnicode']) {
- // With Unicode, widths array isn't used
-
- $this->numObj++;
- $this->o_contents($this->numObj, 'new', 'raw');
- $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
- $widthid = $this->numObj;
- }
-
- $missing_width = 500;
- $stemV = 70;
-
- if (isset($font['MissingWidth'])) {
- $missing_width = $font['MissingWidth'];
- }
- if (isset($font['StdVW'])) {
- $stemV = $font['StdVW'];
- } else {
- if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
- $stemV = 120;
- }
- }
-
- // load the pfb file, and put that into an object too.
- // note that pdf supports only binary format type 1 font files, though there is a
- // simple utility to convert them from pfa to pfb.
- // FIXME: should we move font subset creation to CPDF::output? See notes in issue #750.
- if (!$this->isUnicode || $fbtype !== 'ttf' || empty($this->stringSubsets)) {
- $data = file_get_contents($fbfile);
- } else {
- $this->stringSubsets[$fontName][] = 32; // Force space if not in yet
-
- $subset = $this->stringSubsets[$fontName];
- sort($subset);
-
- // Load font
- $font_obj = Font::load($fbfile);
- $font_obj->parse();
-
- // Define subset
- $font_obj->setSubset($subset);
- $font_obj->reduce();
-
- // Write new font
- $tmp_name = "$fbfile.tmp." . uniqid();
- $font_obj->open($tmp_name, Font_Binary_Stream::modeWrite);
- $font_obj->encode(array("OS/2"));
- $font_obj->close();
-
- // Parse the new font to get cid2gid and widths
- $font_obj = Font::load($tmp_name);
-
- // Find Unicode char map table
- $subtable = null;
- foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
- if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
- $subtable = $_subtable;
- break;
- }
- }
-
- if ($subtable) {
- $glyphIndexArray = $subtable["glyphIndexArray"];
- $hmtx = $font_obj->getData("hmtx");
-
- unset($glyphIndexArray[0xFFFF]);
-
- $cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
- $font['CIDWidths'] = array();
- foreach ($glyphIndexArray as $cid => $gid) {
- if ($cid >= 0 && $cid < 0xFFFF && $gid) {
- $cidtogid[$cid * 2] = chr($gid >> 8);
- $cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
- }
-
- $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
- $font['CIDWidths'][$cid] = $width;
- }
-
- $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
- $font['CIDtoGID_Compressed'] = true;
-
- $data = file_get_contents($tmp_name);
- } else {
- $data = file_get_contents($fbfile);
- }
-
- $font_obj->close();
- unlink($tmp_name);
- }
-
- // create the font descriptor
- $this->numObj++;
- $fontDescriptorId = $this->numObj;
-
- $this->numObj++;
- $pfbid = $this->numObj;
-
- // determine flags (more than a little flakey, hopefully will not matter much)
- $flags = 0;
-
- if ($font['ItalicAngle'] != 0) {
- $flags += pow(2, 6);
- }
-
- if ($font['IsFixedPitch'] === 'true') {
- $flags += 1;
- }
-
- $flags += pow(2, 5); // assume non-sybolic
- $list = array(
- 'Ascent' => 'Ascender',
- 'CapHeight' => 'CapHeight',
- 'MissingWidth' => 'MissingWidth',
- 'Descent' => 'Descender',
- 'FontBBox' => 'FontBBox',
- 'ItalicAngle' => 'ItalicAngle'
- );
- $fdopt = array(
- 'Flags' => $flags,
- 'FontName' => $adobeFontName,
- 'StemV' => $stemV
- );
-
- foreach ($list as $k => $v) {
- if (isset($font[$v])) {
- $fdopt[$k] = $font[$v];
- }
- }
-
- if ($fbtype === 'pfb') {
- $fdopt['FontFile'] = $pfbid;
- } else {
- if ($fbtype === 'ttf') {
- $fdopt['FontFile2'] = $pfbid;
- }
- }
-
- $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
-
- // embed the font program
- $this->o_contents($this->numObj, 'new');
- $this->objects[$pfbid]['c'] .= $data;
-
- // determine the cruicial lengths within this file
- if ($fbtype === 'pfb') {
- $l1 = strpos($data, 'eexec') + 6;
- $l2 = strpos($data, '00000000') - $l1;
- $l3 = mb_strlen($data, '8bit') - $l2 - $l1;
- $this->o_contents(
- $this->numObj,
- 'add',
- array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)
- );
- } else {
- if ($fbtype == 'ttf') {
- $l1 = mb_strlen($data, '8bit');
- $this->o_contents($this->numObj, 'add', array('Length1' => $l1));
- }
- }
-
- // tell the font object about all this new stuff
- $tmp = array(
- 'BaseFont' => $adobeFontName,
- 'MissingWidth' => $missing_width,
- 'Widths' => $widthid,
- 'FirstChar' => $firstChar,
- 'LastChar' => $lastChar,
- 'FontDescriptor' => $fontDescriptorId
- );
-
- if ($fbtype === 'ttf') {
- $tmp['SubType'] = 'TrueType';
- }
-
- $this->addMessage("adding extra info to font.($fontObj)");
-
- foreach ($tmp as $fk => $fv) {
- $this->addMessage("$fk : $fv");
- }
-
- $this->o_font($fontObj, 'add', $tmp);
- } else {
- $this->addMessage(
- 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
- );
- }
+ $font['isSubsetting'] = $isSubsetting && $font['isUnicode'] && strtolower($fileSuffix) === 'ttf';
// also set the differences here, note that this means that these will take effect only the
//first time that a font is selected, else they are ignored
@@ -2676,12 +3606,9 @@ EOT;
// applied to it as well.
$this->currentFont = $this->currentBaseFont;
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
-
- //$this->setCurrentFont();
}
return $this->currentFontNum;
- //return $this->numObj;
}
/**
@@ -2689,7 +3616,7 @@ EOT;
* note that this system is quite flexible, a bold-italic font can be completely different to a
* italic-bold font, and even bold-bold will have to be defined within the family to have meaning
* This function is to be called whenever the currentTextState is changed, it will update
- * the currentFont setting to whatever the appropriatte family one is.
+ * the currentFont setting to whatever the appropriate family one is.
* If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
* This function will change the currentFont to whatever it should be, but will not change the
* currentBaseFont.
@@ -2722,6 +3649,8 @@ EOT;
/**
* function for the user to find out what the ID is of the first page that was created during
* startup - useful if they wish to add something to it later.
+ *
+ * @return int
*/
function getFirstPageId()
{
@@ -2730,6 +3659,8 @@ EOT;
/**
* add content to the currently active object
+ *
+ * @param $content
*/
private function addContent($content)
{
@@ -2738,32 +3669,35 @@ EOT;
/**
* sets the color for fill operations
+ *
+ * @param array $color
+ * @param bool $force
*/
function setColor($color, $force = false)
{
- $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
+ $new_color = [$color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null];
if (!$force && $this->currentColor == $new_color) {
return;
}
if (isset($new_color[3])) {
- //$this->currentColor = $new_color;
+ $this->currentColor = $new_color;
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor));
} else {
if (isset($new_color[2])) {
- //$this->currentColor = $new_color;
- $this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $new_color));
+ $this->currentColor = $new_color;
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor));
}
}
}
/**
- * sets the color for fill operations
+ * @param string $fillRule
*/
function setFillRule($fillRule)
{
- if (!in_array($fillRule, array("nonzero", "evenodd"))) {
+ if (!in_array($fillRule, ["nonzero", "evenodd"])) {
return;
}
@@ -2772,36 +3706,44 @@ EOT;
/**
* sets the color for stroke operations
+ *
+ * @param array $color
+ * @param bool $force
*/
function setStrokeColor($color, $force = false)
{
- $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
+ $new_color = [$color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null];
if (!$force && $this->currentStrokeColor == $new_color) {
return;
}
if (isset($new_color[3])) {
- //$this->currentStrokeColor = $new_color;
+ $this->currentStrokeColor = $new_color;
$this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor));
} else {
if (isset($new_color[2])) {
- //$this->currentStrokeColor = $new_color;
- $this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $new_color));
+ $this->currentStrokeColor = $new_color;
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor));
}
}
}
/**
* Set the graphics state for compositions
+ *
+ * @param $parameters
*/
function setGraphicsState($parameters)
{
- // Create a new graphics state object
- // FIXME: should actually keep track of states that have already been created...
- $this->numObj++;
- $this->o_extGState($this->numObj, 'new', $parameters);
- $this->addContent("\n/GS$this->numStates gs");
+ // Create a new graphics state object if necessary
+ if (($gstate = array_search($parameters, $this->gstates)) === false) {
+ $this->numObj++;
+ $this->o_extGState($this->numObj, 'new', $parameters);
+ $gstate = $this->numStates;
+ $this->gstates[$gstate] = $parameters;
+ }
+ $this->addContent("\n/GS$gstate gs");
}
/**
@@ -2818,7 +3760,7 @@ EOT;
*/
function setLineTransparency($mode, $opacity)
{
- static $blend_modes = array(
+ static $blend_modes = [
"Normal",
"Multiply",
"Screen",
@@ -2831,26 +3773,30 @@ EOT;
"SoftLight",
"Difference",
"Exclusion"
- );
+ ];
if (!in_array($mode, $blend_modes)) {
$mode = "Normal";
}
- // Only create a new graphics state if required
- if ($mode === $this->currentLineTransparency["mode"] &&
- $opacity == $this->currentLineTransparency["opacity"]
- ) {
+ if (is_null($this->currentLineTransparency)) {
+ $this->currentLineTransparency = [];
+ }
+
+ if ($mode === (key_exists('mode', $this->currentLineTransparency) ?
+ $this->currentLineTransparency['mode'] : '') &&
+ $opacity === (key_exists('opacity', $this->currentLineTransparency) ?
+ $this->currentLineTransparency["opacity"] : '')) {
return;
}
$this->currentLineTransparency["mode"] = $mode;
$this->currentLineTransparency["opacity"] = $opacity;
- $options = array(
+ $options = [
"BM" => "/$mode",
"CA" => (float)$opacity
- );
+ ];
$this->setGraphicsState($options);
}
@@ -2869,7 +3815,7 @@ EOT;
*/
function setFillTransparency($mode, $opacity)
{
- static $blend_modes = array(
+ static $blend_modes = [
"Normal",
"Multiply",
"Screen",
@@ -2882,34 +3828,120 @@ EOT;
"SoftLight",
"Difference",
"Exclusion"
- );
+ ];
if (!in_array($mode, $blend_modes)) {
$mode = "Normal";
}
- if ($mode === $this->currentFillTransparency["mode"] &&
- $opacity == $this->currentFillTransparency["opacity"]
- ) {
+ if (is_null($this->currentFillTransparency)) {
+ $this->currentFillTransparency = [];
+ }
+
+ if ($mode === (key_exists('mode', $this->currentFillTransparency) ?
+ $this->currentFillTransparency['mode'] : '') &&
+ $opacity === (key_exists('opacity', $this->currentFillTransparency) ?
+ $this->currentFillTransparency["opacity"] : '')) {
return;
}
$this->currentFillTransparency["mode"] = $mode;
$this->currentFillTransparency["opacity"] = $opacity;
- $options = array(
+ $options = [
"BM" => "/$mode",
"ca" => (float)$opacity,
- );
+ ];
$this->setGraphicsState($options);
}
+ /**
+ * draw a line from one set of coordinates to another
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $x2
+ * @param float $y2
+ * @param bool $stroke
+ */
+ function line($x1, $y1, $x2, $y2, $stroke = true)
+ {
+ $this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
+
+ if ($stroke) {
+ $this->addContent(' S');
+ }
+ }
+
+ /**
+ * draw a bezier curve based on 4 control points
+ *
+ * @param float $x0
+ * @param float $y0
+ * @param float $x1
+ * @param float $y1
+ * @param float $x2
+ * @param float $y2
+ * @param float $x3
+ * @param float $y3
+ */
+ function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
+ {
+ // in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
+ // as the control points for the curve.
+ $this->addContent(
+ sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
+ );
+ }
+
+ /**
+ * draw a part of an ellipse
+ *
+ * @param float $x0
+ * @param float $y0
+ * @param float $astart
+ * @param float $afinish
+ * @param float $r1
+ * @param float $r2
+ * @param float $angle
+ * @param int $nSeg
+ */
+ function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
+ {
+ $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
+ }
+
+ /**
+ * draw a filled ellipse
+ *
+ * @param float $x0
+ * @param float $y0
+ * @param float $r1
+ * @param float $r2
+ * @param float $angle
+ * @param int $nSeg
+ * @param float $astart
+ * @param float $afinish
+ */
+ function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
+ {
+ $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
+ }
+
+ /**
+ * @param float $x
+ * @param float $y
+ */
function lineTo($x, $y)
{
$this->addContent(sprintf("\n%.3F %.3F l", $x, $y));
}
+ /**
+ * @param float $x
+ * @param float $y
+ */
function moveTo($x, $y)
{
$this->addContent(sprintf("\n%.3F %.3F m", $x, $y));
@@ -2917,6 +3949,13 @@ EOT;
/**
* draw a bezier curve based on 4 control points
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $x2
+ * @param float $y2
+ * @param float $x3
+ * @param float $y3
*/
function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
{
@@ -2925,6 +3964,11 @@ EOT;
/**
* draw a bezier curve based on 4 control points
+ *
+ * @param float $cpx
+ * @param float $cpy
+ * @param float $x
+ * @param float $y
*/
function quadTo($cpx, $cpy, $x, $y)
{
@@ -2951,6 +3995,19 @@ EOT;
* from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
* nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
* pretty crappy shape at 2, as we are approximating with bezier curves.
+ *
+ * @param float $x0
+ * @param float $y0
+ * @param float $r1
+ * @param float $r2
+ * @param float $angle
+ * @param int $nSeg
+ * @param float $astart
+ * @param float $afinish
+ * @param bool $close
+ * @param bool $fill
+ * @param bool $stroke
+ * @param bool $incomplete
*/
function ellipse(
$x0,
@@ -3063,6 +4120,12 @@ EOT;
* (2) represents 2 on, 2 off, 2 on , 2 off ...
* (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
* phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
+ *
+ * @param float $width
+ * @param string $cap
+ * @param string $join
+ * @param array $dash
+ * @param int $phase
*/
function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
{
@@ -3070,16 +4133,16 @@ EOT;
$string = '';
if ($width > 0) {
- $string .= sprintf("%.3F w", $width);
+ $string .= "$width w";
}
- $ca = array('butt' => 0, 'round' => 1, 'square' => 2);
+ $ca = ['butt' => 0, 'round' => 1, 'square' => 2];
if (isset($ca[$cap])) {
$string .= " $ca[$cap] J";
}
- $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
+ $ja = ['miter' => 0, 'round' => 1, 'bevel' => 2];
if (isset($ja[$join])) {
$string .= " $ja[$join] j";
@@ -3093,6 +4156,65 @@ EOT;
$this->addContent("\n$string");
}
+ /**
+ * draw a polygon, the syntax for this is similar to the GD polygon command
+ *
+ * @param float[] $p
+ * @param bool $fill
+ */
+ public function polygon(array $p, bool $fill = false): void
+ {
+ $this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
+
+ $n = count($p);
+ for ($i = 2; $i < $n; $i = $i + 2) {
+ $this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
+ }
+
+ if ($fill) {
+ $this->addContent(' f');
+ } else {
+ $this->addContent(' S');
+ }
+ }
+
+ /**
+ * a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
+ * the coordinates of the upper-right corner
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $width
+ * @param float $height
+ */
+ function filledRectangle($x1, $y1, $width, $height)
+ {
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
+ }
+
+ /**
+ * draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
+ * the coordinates of the upper-right corner
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $width
+ * @param float $height
+ */
+ function rectangle($x1, $y1, $width, $height)
+ {
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
+ }
+
+ /**
+ * draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
+ * the coordinates of the upper-right corner
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $width
+ * @param float $height
+ */
function rect($x1, $y1, $width, $height)
{
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height));
@@ -3105,12 +4227,195 @@ EOT;
function fill()
{
- $this->addContent("\nf".($this->fillRule === "evenodd" ? "*" : ""));
+ $this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : ""));
}
function fillStroke()
{
- $this->addContent("\nb".($this->fillRule === "evenodd" ? "*" : ""));
+ $this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : ""));
+ }
+
+ /**
+ * @param string $subtype
+ * @param integer $x
+ * @param integer $y
+ * @param integer $w
+ * @param integer $h
+ * @return int
+ */
+ function addXObject($subtype, $x, $y, $w, $h)
+ {
+ $id = ++$this->numObj;
+ $this->o_xobject($id, 'new', ['Subtype' => $subtype, 'bbox' => [$x, $y, $w, $h]]);
+ return $id;
+ }
+
+ /**
+ * @param integer $numXObject
+ * @param string $type
+ * @param array $options
+ */
+ function setXObjectResource($numXObject, $type, $options)
+ {
+ if (in_array($type, ['procset', 'font', 'xObject'])) {
+ $this->o_xobject($numXObject, $type, $options);
+ }
+ }
+
+ /**
+ * add signature
+ *
+ * $fieldSigId = $cpdf->addFormField(Cpdf::ACROFORM_FIELD_SIG, 'Signature1', 0, 0, 0, 0, 0);
+ *
+ * $signatureId = $cpdf->addSignature([
+ * 'signcert' => file_get_contents('dompdf.crt'),
+ * 'privkey' => file_get_contents('dompdf.key'),
+ * 'password' => 'password',
+ * 'name' => 'DomPDF DEMO',
+ * 'location' => 'Home',
+ * 'reason' => 'First Form',
+ * 'contactinfo' => 'info'
+ * ]);
+ * $cpdf->setFormFieldValue($fieldSigId, "$signatureId 0 R");
+ *
+ * @param string $signcert
+ * @param string $privkey
+ * @param string $password
+ * @param string|null $name
+ * @param string|null $location
+ * @param string|null $reason
+ * @param string|null $contactinfo
+ * @return int
+ */
+ function addSignature($signcert, $privkey, $password = '', $name = null, $location = null, $reason = null, $contactinfo = null) {
+ $sigId = ++$this->numObj;
+ $this->o_sig($sigId, 'new', [
+ 'SignCert' => $signcert,
+ 'PrivKey' => $privkey,
+ 'Password' => $password,
+ 'Name' => $name,
+ 'Location' => $location,
+ 'Reason' => $reason,
+ 'ContactInfo' => $contactinfo
+ ]);
+
+ return $sigId;
+ }
+
+ /**
+ * add field to form
+ *
+ * @param string $type ACROFORM_FIELD_*
+ * @param string $name
+ * @param $x0
+ * @param $y0
+ * @param $x1
+ * @param $y1
+ * @param integer $ff Field Flag ACROFORM_FIELD_*_*
+ * @param float $size
+ * @param array $color
+ * @return int
+ */
+ public function addFormField($type, $name, $x0, $y0, $x1, $y1, $ff = 0, $size = 10.0, $color = [0, 0, 0])
+ {
+ if (!$this->numFonts) {
+ $this->selectFont($this->defaultFont);
+ }
+
+ $color = implode(' ', $color) . ' rg';
+
+ $currentFontNum = $this->currentFontNum;
+ $font = array_filter(
+ $this->objects[$this->currentNode]['info']['fonts'],
+ function ($item) use ($currentFontNum) { return $item['fontNum'] == $currentFontNum; }
+ );
+
+ $this->o_acroform($this->acroFormId, 'font',
+ ['objNum' => $font[0]['objNum'], 'fontNum' => $font[0]['fontNum']]);
+
+ $fieldId = ++$this->numObj;
+ $this->o_field($fieldId, 'new', [
+ 'rect' => [$x0, $y0, $x1, $y1],
+ 'F' => 4,
+ 'FT' => "/$type",
+ 'T' => $name,
+ 'Ff' => $ff,
+ 'pageid' => $this->currentPage,
+ 'da' => "$color /F$this->currentFontNum " . sprintf('%.1F Tf ', $size)
+ ]);
+
+ return $fieldId;
+ }
+
+ /**
+ * set Field value
+ *
+ * @param integer $numFieldObj
+ * @param string $value
+ */
+ public function setFormFieldValue($numFieldObj, $value)
+ {
+ $this->o_field($numFieldObj, 'set', ['value' => $value]);
+ }
+
+ /**
+ * set Field value (reference)
+ *
+ * @param integer $numFieldObj
+ * @param integer $numObj Object number
+ */
+ public function setFormFieldRefValue($numFieldObj, $numObj)
+ {
+ $this->o_field($numFieldObj, 'set', ['refvalue' => $numObj]);
+ }
+
+ /**
+ * set Field Appearanc (reference)
+ *
+ * @param integer $numFieldObj
+ * @param integer $normalNumObj
+ * @param integer|null $rolloverNumObj
+ * @param integer|null $downNumObj
+ */
+ public function setFormFieldAppearance($numFieldObj, $normalNumObj, $rolloverNumObj = null, $downNumObj = null)
+ {
+ $appearance['N'] = $normalNumObj;
+
+ if ($rolloverNumObj !== null) {
+ $appearance['R'] = $rolloverNumObj;
+ }
+
+ if ($downNumObj !== null) {
+ $appearance['D'] = $downNumObj;
+ }
+
+ $this->o_field($numFieldObj, 'set', ['appearance' => $appearance]);
+ }
+
+ /**
+ * set Choice Field option values
+ *
+ * @param integer $numFieldObj
+ * @param array $value
+ */
+ public function setFormFieldOpt($numFieldObj, $value)
+ {
+ $this->o_field($numFieldObj, 'set', ['options' => $value]);
+ }
+
+ /**
+ * add form to document
+ *
+ * @param integer $sigFlags
+ * @param boolean $needAppearances
+ */
+ public function addForm($sigFlags = 0, $needAppearances = false)
+ {
+ $this->acroFormId = ++$this->numObj;
+ $this->o_acroform($this->acroFormId, 'new', [
+ 'NeedAppearances' => $needAppearances ? 'true' : 'false',
+ 'SigFlags' => $sigFlags
+ ]);
}
/**
@@ -3118,6 +4423,9 @@ EOT;
*/
function save()
{
+ // we must reset the color cache or it will keep bad colors after clipping
+ $this->currentColor = null;
+ $this->currentStrokeColor = null;
$this->addContent("\nq");
}
@@ -3126,26 +4434,123 @@ EOT;
*/
function restore()
{
+ // we must reset the color cache or it will keep bad colors after clipping
+ $this->currentColor = null;
+ $this->currentStrokeColor = null;
$this->addContent("\nQ");
}
+ /**
+ * draw a clipping rectangle, all the elements added after this will be clipped
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $width
+ * @param float $height
+ */
+ function clippingRectangle($x1, $y1, $width, $height)
+ {
+ $this->save();
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
+ }
+
+ /**
+ * draw a clipping rounded rectangle, all the elements added after this will be clipped
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $w
+ * @param float $h
+ * @param float $rTL
+ * @param float $rTR
+ * @param float $rBR
+ * @param float $rBL
+ */
+ function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
+ {
+ $this->save();
+
+ // start: top edge, left end
+ $this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
+
+ // line: bottom edge, left end
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1, $y1 + $rBL));
+
+ // curve: bottom-left corner
+ $this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
+
+ // line: right edge, bottom end
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
+
+ // curve: bottom-right corner
+ $this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
+
+ // line: right edge, top end
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
+
+ // curve: bottom-right corner
+ $this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
+
+ // line: bottom edge, right end
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
+
+ // curve: top-right corner
+ $this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
+
+ // line: top edge, left end
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
+
+ // Close & clip
+ $this->addContent(" W n");
+ }
+
+ /**
+ * draw a clipping polygon, the syntax for this is similar to the GD polygon command
+ *
+ * @param float[] $p
+ */
+ public function clippingPolygon(array $p): void
+ {
+ $this->save();
+
+ $this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
+
+ $n = count($p);
+ for ($i = 2; $i < $n; $i = $i + 2) {
+ $this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
+ }
+
+ $this->addContent("W n");
+ }
+
+ /**
+ * ends the last clipping shape
+ */
+ function clippingEnd()
+ {
+ $this->restore();
+ }
+
/**
* scale
*
* @param float $s_x scaling factor for width as percent
* @param float $s_y scaling factor for height as percent
- * @param float $x Origin abscisse
+ * @param float $x Origin abscissa
* @param float $y Origin ordinate
*/
function scale($s_x, $s_y, $x, $y)
{
$y = $this->currentPageSize["height"] - $y;
- $tm = array(
- $s_x, 0,
- 0, $s_y,
- $x * (1 - $s_x), $y * (1 - $s_y)
- );
+ $tm = [
+ $s_x,
+ 0,
+ 0,
+ $s_y,
+ $x * (1 - $s_x),
+ $y * (1 - $s_y)
+ ];
$this->transform($tm);
}
@@ -3158,11 +4563,14 @@ EOT;
*/
function translate($t_x, $t_y)
{
- $tm = array(
- 1, 0,
- 0, 1,
- $t_x, -$t_y
- );
+ $tm = [
+ 1,
+ 0,
+ 0,
+ 1,
+ $t_x,
+ -$t_y
+ ];
$this->transform($tm);
}
@@ -3171,7 +4579,7 @@ EOT;
* rotate
*
* @param float $angle angle in degrees for counter-clockwise rotation
- * @param float $x Origin abscisse
+ * @param float $x Origin abscissa
* @param float $y Origin ordinate
*/
function rotate($angle, $x, $y)
@@ -3182,11 +4590,14 @@ EOT;
$cos_a = cos($a);
$sin_a = sin($a);
- $tm = array(
- $cos_a, -$sin_a,
- $sin_a, $cos_a,
- $x - $sin_a * $y - $cos_a * $x, $y - $cos_a * $y + $sin_a * $x,
- );
+ $tm = [
+ $cos_a,
+ -$sin_a,
+ $sin_a,
+ $cos_a,
+ $x - $sin_a * $y - $cos_a * $x,
+ $y - $cos_a * $y + $sin_a * $x,
+ ];
$this->transform($tm);
}
@@ -3196,7 +4607,7 @@ EOT;
*
* @param float $angle_x
* @param float $angle_y
- * @param float $x Origin abscisse
+ * @param float $x Origin abscissa
* @param float $y Origin ordinate
*/
function skew($angle_x, $angle_y, $x, $y)
@@ -3206,11 +4617,14 @@ EOT;
$tan_x = tan(deg2rad($angle_x));
$tan_y = tan(deg2rad($angle_y));
- $tm = array(
- 1, -$tan_y,
- -$tan_x, 1,
- $tan_x * $y, $tan_y * $x,
- );
+ $tm = [
+ 1,
+ -$tan_y,
+ -$tan_x,
+ 1,
+ $tan_x * $y,
+ $tan_y * $x,
+ ];
$this->transform($tm);
}
@@ -3228,6 +4642,11 @@ EOT;
/**
* add a new page to the document
* this also makes the new page the current active object
+ *
+ * @param int $insert
+ * @param int $id
+ * @param string $pos
+ * @return int
*/
function newPage($insert = 0, $id = 0, $pos = 'after')
{
@@ -3246,7 +4665,7 @@ EOT;
// the id from the ezPdf class is the id of the contents of the page, not the page object itself
// query that object to find the parent
$rid = $this->objects[$id]['onPage'];
- $opt = array('rid' => $rid, 'pos' => $pos);
+ $opt = ['rid' => $rid, 'pos' => $pos];
$this->o_page($this->numObj, 'new', $opt);
} else {
$this->o_page($this->numObj, 'new');
@@ -3278,58 +4697,40 @@ EOT;
}
/**
- * output the pdf code, streaming it to the browser
- * the relevant headers are set so that hopefully the browser will recognise it
+ * Streams the PDF to the client.
+ *
+ * @param string $filename The filename to present to the client.
+ * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
*/
- function stream($options = '')
+ function stream($filename = "document.pdf", $options = [])
{
- // setting the options allows the adjustment of the headers
- // values at the moment are:
- // 'Content-Disposition' => 'filename' - sets the filename, though not too sure how well this will
- // work as in my trial the browser seems to use the filename of the php file with .pdf on the end
- // 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this header is not included, off by default
- // this header seems to have caused some problems despite tha fact that it is supposed to solve
- // them, so I am leaving it off by default.
- // 'compress' = > 1 or 0 - apply content stream compression, this is on (1) by default
- // 'Attachment' => 1 or 0 - if 1, force the browser to open a download dialog
- if (!is_array($options)) {
- $options = array();
- }
-
if (headers_sent()) {
die("Unable to stream pdf: headers already sent");
}
- $debug = empty($options['compression']);
+ if (!isset($options["compress"])) $options["compress"] = true;
+ if (!isset($options["Attachment"])) $options["Attachment"] = true;
+
+ $debug = !$options['compress'];
$tmp = ltrim($this->output($debug));
header("Cache-Control: private");
- header("Content-type: application/pdf");
-
- //FIXME: I don't know that this is sufficient for determining content length (i.e. what about transport compression?)
- header("Content-Length: " . mb_strlen($tmp, '8bit'));
- $fileName = (isset($options['Content-Disposition']) ? $options['Content-Disposition'] : 'file.pdf');
-
- if (!isset($options["Attachment"])) {
- $options["Attachment"] = true;
- }
+ header("Content-Type: application/pdf");
+ header("Content-Length: " . mb_strlen($tmp, "8bit"));
+ $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf";
$attachment = $options["Attachment"] ? "attachment" : "inline";
- // detect the character encoding of the incoming file
- $encoding = mb_detect_encoding($fileName);
- $fallbackfilename = mb_convert_encoding($fileName, "ISO-8859-1", $encoding);
- $encodedfallbackfilename = rawurlencode($fallbackfilename);
- $encodedfilename = rawurlencode($fileName);
+ $encoding = mb_detect_encoding($filename);
+ $fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding);
+ $fallbackfilename = str_replace("\"", "", $fallbackfilename);
+ $encodedfilename = rawurlencode($filename);
- header(
- "Content-Disposition: $attachment; filename=" . $encodedfallbackfilename . "; filename*=UTF-8''$encodedfilename"
- );
-
- if (isset($options['Accept-Ranges']) && $options['Accept-Ranges'] == 1) {
- //FIXME: Is this the correct value ... spec says 1#range-unit
- header("Accept-Ranges: " . mb_strlen($tmp, '8bit'));
+ $contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\"";
+ if ($fallbackfilename !== $filename) {
+ $contentDisposition .= "; filename*=UTF-8''$encodedfilename";
}
+ header($contentDisposition);
echo $tmp;
flush();
@@ -3337,8 +4738,12 @@ EOT;
/**
* return the height in units of the current font in the given size
+ *
+ * @param float $size
+ *
+ * @return float
*/
- function getFontHeight($size)
+ public function getFontHeight(float $size): float
{
if (!$this->numFonts) {
$this->selectFont($this->defaultFont);
@@ -3369,7 +4774,12 @@ EOT;
return $size * $h / 1000;
}
- function getFontXHeight($size)
+ /**
+ * @param float $size
+ *
+ * @return float
+ */
+ public function getFontXHeight(float $size): float
{
if (!$this->numFonts) {
$this->selectFont($this->defaultFont);
@@ -3391,8 +4801,12 @@ EOT;
* return the font descender, this will normally return a negative number
* if you add this number to the baseline, you get the level of the bottom of the font
* it is in the pdf user units
+ *
+ * @param float $size
+ *
+ * @return float
*/
- function getFontDescender($size)
+ public function getFontDescender(float $size): float
{
// note that this will most likely return a negative value
if (!$this->numFonts) {
@@ -3409,7 +4823,10 @@ EOT;
* filter the text, this is applied to all text just before being inserted into the pdf document
* it escapes the various things that need to be escaped, and so on
*
- * @access private
+ * @param $text
+ * @param bool $bom
+ * @param bool $convert_encoding
+ * @return string
*/
function filterText($text, $bom = true, $convert_encoding = true)
{
@@ -3420,21 +4837,132 @@ EOT;
if ($convert_encoding) {
$cf = $this->currentFont;
if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
- //$text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
$text = $this->utf8toUtf16BE($text, $bom);
} else {
//$text = html_entity_decode($text, ENT_QUOTES);
$text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
}
+ } elseif ($bom) {
+ $text = $this->utf8toUtf16BE($text, $bom);
}
// the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
- return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
+ return strtr($text, [')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r']);
+ }
+
+ /**
+ * return array containing codepoints (UTF-8 character values) for the
+ * string passed in.
+ *
+ * based on the excellent TCPDF code by Nicola Asuni and the
+ * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
+ *
+ * @param string $text UTF-8 string to process
+ * @return array UTF-8 codepoints array for the string
+ */
+ function utf8toCodePointsArray(&$text)
+ {
+ $length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
+ $unicode = []; // array containing unicode values
+ $bytes = []; // array containing single character byte sequences
+ $numbytes = 1; // number of octets needed to represent the UTF-8 character
+
+ for ($i = 0; $i < $length; $i++) {
+ $c = ord($text[$i]); // get one string character at time
+ if (count($bytes) === 0) { // get starting octect
+ if ($c <= 0x7F) {
+ $unicode[] = $c; // use the character "as is" because is ASCII
+ $numbytes = 1;
+ } elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
+ $bytes[] = ($c - 0xC0) << 0x06;
+ $numbytes = 2;
+ } elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
+ $bytes[] = ($c - 0xE0) << 0x0C;
+ $numbytes = 3;
+ } elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
+ $bytes[] = ($c - 0xF0) << 0x12;
+ $numbytes = 4;
+ } else {
+ // use replacement character for other invalid sequences
+ $unicode[] = 0xFFFD;
+ $bytes = [];
+ $numbytes = 1;
+ }
+ } elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
+ $bytes[] = $c - 0x80;
+ if (count($bytes) === $numbytes) {
+ // compose UTF-8 bytes to a single unicode value
+ $c = $bytes[0];
+ for ($j = 1; $j < $numbytes; $j++) {
+ $c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
+ }
+ if ((($c >= 0xD800) and ($c <= 0xDFFF)) or ($c >= 0x10FFFF)) {
+ // The definition of UTF-8 prohibits encoding character numbers between
+ // U+D800 and U+DFFF, which are reserved for use with the UTF-16
+ // encoding form (as surrogate pairs) and do not directly represent
+ // characters.
+ $unicode[] = 0xFFFD; // use replacement character
+ } else {
+ $unicode[] = $c; // add char to array
+ }
+ // reset data for next char
+ $bytes = [];
+ $numbytes = 1;
+ }
+ } else {
+ // use replacement character for other invalid sequences
+ $unicode[] = 0xFFFD;
+ $bytes = [];
+ $numbytes = 1;
+ }
+ }
+
+ return $unicode;
+ }
+
+ /**
+ * convert UTF-8 to UTF-16 with an additional byte order marker
+ * at the front if required.
+ *
+ * based on the excellent TCPDF code by Nicola Asuni and the
+ * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
+ *
+ * @param string $text UTF-8 string to process
+ * @param boolean $bom whether to add the byte order marker
+ * @return string UTF-16 result string
+ */
+ function utf8toUtf16BE(&$text, $bom = true)
+ {
+ $out = $bom ? "\xFE\xFF" : '';
+
+ $unicode = $this->utf8toCodePointsArray($text);
+ foreach ($unicode as $c) {
+ if ($c === 0xFFFD) {
+ $out .= "\xFF\xFD"; // replacement character
+ } elseif ($c < 0x10000) {
+ $out .= chr($c >> 0x08) . chr($c & 0xFF);
+ } else {
+ $c -= 0x10000;
+ $w1 = 0xD800 | ($c >> 0x10);
+ $w2 = 0xDC00 | ($c & 0x3FF);
+ $out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
+ }
+ }
+
+ return $out;
}
/**
* given a start position and information about how text is to be laid out, calculate where
* on the page the text will end
+ *
+ * @param $x
+ * @param $y
+ * @param $angle
+ * @param $size
+ * @param $wa
+ * @param $text
+ * @return array
*/
private function getTextPosition($x, $y, $angle, $size, $wa, $text)
{
@@ -3447,7 +4975,7 @@ EOT;
$w += $wa * $nspaces;
$a = deg2rad((float)$angle);
- return array(cos($a) * $w + $x, -sin($a) * $w + $y);
+ return [cos($a) * $w + $x, -sin($a) * $w + $y];
}
/**
@@ -3473,7 +5001,10 @@ EOT;
}
/**
- * add text to the document, at a specified location, size and angle on the page
+ * register text for font subsetting
+ *
+ * @param string $font
+ * @param string $text
*/
function registerText($font, $text)
{
@@ -3482,7 +5013,8 @@ EOT;
}
if (!isset($this->stringSubsets[$font])) {
- $this->stringSubsets[$font] = array();
+ $base_subset = "\u{fffd}\u{fffe}\u{ffff}";
+ $this->stringSubsets[$font] = $this->utf8toCodePointsArray($base_subset);
}
$this->stringSubsets[$font] = array_unique(
@@ -3492,6 +5024,15 @@ EOT;
/**
* add text to the document, at a specified location, size and angle on the page
+ *
+ * @param float $x
+ * @param float $y
+ * @param float $size
+ * @param string $text
+ * @param float $angle
+ * @param float $wordSpaceAdjust
+ * @param float $charSpaceAdjust
+ * @param bool $smallCaps
*/
function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
{
@@ -3499,25 +5040,25 @@ EOT;
$this->selectFont($this->defaultFont);
}
- $text = str_replace(array("\r", "\n"), "", $text);
+ $text = str_replace(["\r", "\n"], "", $text);
- if ($smallCaps) {
- preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
- $lower = $this->concatMatches($matches);
- d($lower);
+ // if ($smallCaps) {
+ // preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
+ // $lower = $this->concatMatches($matches);
+ // d($lower);
- preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
- $other = $this->concatMatches($matches);
- d($other);
+ // preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
+ // $other = $this->concatMatches($matches);
+ // d($other);
- //$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
- }
+ // $text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
+ // }
// if there are any open callbacks, then they should be called, to show the start of the line
if ($this->nCallback > 0) {
for ($i = $this->nCallback; $i > 0; $i--) {
// call each function
- $info = array(
+ $info = [
'x' => $x,
'y' => $y,
'angle' => $angle,
@@ -3526,7 +5067,7 @@ EOT;
'nCallback' => $this->callback[$i]['nCallback'],
'height' => $this->callback[$i]['height'],
'descender' => $this->callback[$i]['descender']
- );
+ ];
$func = $this->callback[$i]['f'];
$this->$func($info);
@@ -3542,13 +5083,11 @@ EOT;
);
}
- if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust) {
- $this->wordSpaceAdjust = $wordSpaceAdjust;
+ if ($wordSpaceAdjust != 0) {
$this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
}
- if ($charSpaceAdjust != 0 || $charSpaceAdjust != $this->charSpaceAdjust) {
- $this->charSpaceAdjust = $charSpaceAdjust;
+ if ($charSpaceAdjust != 0) {
$this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
}
@@ -3559,16 +5098,22 @@ EOT;
$part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
$place_text = $this->filterText($part, false);
// modify unicode text so that extra word spacing is manually implemented (bug #)
- $cf = $this->currentFont;
- if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
+ if ($this->fonts[$this->currentFont]['isUnicode'] && $wordSpaceAdjust != 0) {
$space_scale = 1000 / $size;
- //$place_text = str_replace(' ', ') ( ) '.($this->getTextWidth($size, chr(32), $wordSpaceAdjust)*-75).' (', $place_text);
- $place_text = str_replace(' ', ' ) ' . (-round($space_scale * $wordSpaceAdjust)) . ' (', $place_text);
+ $place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text);
}
$this->addContent(" /F$this->currentFontNum " . sprintf('%.1F Tf ', $size));
$this->addContent(" [($place_text)] TJ");
}
+ if ($wordSpaceAdjust != 0) {
+ $this->addContent(sprintf(" %.3F Tw", 0));
+ }
+
+ if ($charSpaceAdjust != 0) {
+ $this->addContent(sprintf(" %.3F Tc", 0));
+ }
+
$this->addContent(' ET');
// if there are any open callbacks, then they should be called, to show the end of the line
@@ -3576,7 +5121,7 @@ EOT;
for ($i = $this->nCallback; $i > 0; $i--) {
// call each function
$tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
- $info = array(
+ $info = [
'x' => $tmp[0],
'y' => $tmp[1],
'angle' => $angle,
@@ -3585,20 +5130,31 @@ EOT;
'nCallback' => $this->callback[$i]['nCallback'],
'height' => $this->callback[$i]['height'],
'descender' => $this->callback[$i]['descender']
- );
+ ];
$func = $this->callback[$i]['f'];
$this->$func($info);
}
}
+
+ if ($this->fonts[$this->currentFont]['isSubsetting']) {
+ $this->registerText($this->currentFont, $text);
+ }
}
/**
* calculate how wide a given text string will be on a page, at a given size.
* this can be called externally, but is also used by the other class functions
+ *
+ * @param float $size
+ * @param string $text
+ * @param float $wordSpacing
+ * @param float $charSpacing
+ *
+ * @return float
*/
- function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0)
+ public function getTextWidth(float $size, string $text, float $wordSpacing = 0.0, float $charSpacing = 0.0): float
{
- static $ord_cache = array();
+ static $ord_cache = [];
// this function should not change any of the settings, though it will need to
// track any directives which change during calculation, so copy them at the start
@@ -3609,10 +5165,7 @@ EOT;
$this->selectFont($this->defaultFont);
}
- $text = str_replace(array("\r", "\n"), "", $text);
-
- // converts a number or a float to a string so it can get the width
- $text = "$text";
+ $text = str_replace(["\r", "\n"], "", $text);
// hmm, this is where it all starts to get tricky - use the font information to
// calculate the width of each character, add them up and convert to user units
@@ -3620,7 +5173,6 @@ EOT;
$cf = $this->currentFont;
$current_font = $this->fonts[$cf];
$space_scale = 1000 / ($size > 0 ? $size : 1);
- $n_spaces = 0;
if ($current_font['isUnicode']) {
// for Unicode, use the code points array to calculate width rather
@@ -3641,15 +5193,14 @@ EOT;
// add additional padding for space
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
- $w += $word_spacing * $space_scale;
- $n_spaces++;
+ $w += $wordSpacing * $space_scale;
}
}
}
- // add additionnal char spacing
- if ($char_spacing != 0) {
- $w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
+ // add additional char spacing
+ if ($charSpacing != 0) {
+ $w += $charSpacing * $space_scale * count($unicode);
}
} else {
@@ -3677,15 +5228,14 @@ EOT;
// add additional padding for space
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
- $w += $word_spacing * $space_scale;
- $n_spaces++;
+ $w += $wordSpacing * $space_scale;
}
}
}
- // add additionnal char spacing
- if ($char_spacing != 0) {
- $w += $char_spacing * $space_scale * ($len + $n_spaces);
+ // add additional char spacing
+ if ($charSpacing != 0) {
+ $w += $charSpacing * $space_scale * $len;
}
}
@@ -3700,6 +5250,7 @@ EOT;
* end of the previous page, before the stack was closed down
* This is to get around not being able to have open 'q' across pages
*
+ * @param int $pageEnd
*/
function saveState($pageEnd = 0)
{
@@ -3715,11 +5266,11 @@ EOT;
// $this->currentLineStyle = $opt['lin'];
} else {
$this->nStateStack++;
- $this->stateStack[$this->nStateStack] = array(
+ $this->stateStack[$this->nStateStack] = [
'col' => $this->currentColor,
'str' => $this->currentStrokeColor,
'lin' => $this->currentLineStyle
- );
+ ];
}
$this->save();
@@ -3727,6 +5278,8 @@ EOT;
/**
* restore a previously saved state
+ *
+ * @param int $pageEnd
*/
function restoreState($pageEnd = 0)
{
@@ -3749,11 +5302,13 @@ EOT;
* the current one.
* this object will not appear until it is included within a page.
* the function will return the object number
+ *
+ * @return int
*/
function openObject()
{
$this->nStack++;
- $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
+ $this->stack[$this->nStack] = ['c' => $this->currentContents, 'p' => $this->currentPage];
// add a new object of the content type, to hold the data flow
$this->numObj++;
$this->o_contents($this->numObj, 'new');
@@ -3765,11 +5320,13 @@ EOT;
/**
* open an existing object for editing
+ *
+ * @param $id
*/
function reopenObject($id)
{
$this->nStack++;
- $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
+ $this->stack[$this->nStack] = ['c' => $this->currentContents, 'p' => $this->currentPage];
$this->currentContents = $id;
// also if this object is the primary contents for a page, then set the current page to its parent
@@ -3796,11 +5353,13 @@ EOT;
/**
* stop an object from appearing on pages from this point on
+ *
+ * @param $id
*/
function stopObject($id)
{
// if an object has been appearing on pages up to now, then stop it, this page will
- // be the last one that could contian it.
+ // be the last one that could contain it.
if (isset($this->addLooseObjects[$id])) {
$this->addLooseObjects[$id] = '';
}
@@ -3808,6 +5367,9 @@ EOT;
/**
* after an object has been created, it wil only show if it has been added, using this function.
+ *
+ * @param $id
+ * @param string $options
*/
function addObject($id, $options = 'add')
{
@@ -3863,16 +5425,24 @@ EOT;
/**
* return a storable representation of a specific object
+ *
+ * @param $id
+ * @return string|null
*/
function serializeObject($id)
{
if (array_key_exists($id, $this->objects)) {
return serialize($this->objects[$id]);
}
+
+ return null;
}
/**
- * restore an object from its stored representation. returns its new object id.
+ * restore an object from its stored representation. Returns its new object id.
+ *
+ * @param $obj
+ * @return int
*/
function restoreSerializedObject($obj)
{
@@ -3884,9 +5454,33 @@ EOT;
}
/**
- * add content to the documents info object
+ * Embeds a file inside the PDF
+ *
+ * @param string $filepath path to the file to store inside the PDF
+ * @param string $embeddedFilename the filename displayed in the list of embedded files
+ * @param string $description a description in the list of embedded files
*/
- function addInfo($label, $value = 0)
+ public function addEmbeddedFile(string $filepath, string $embeddedFilename, string $description): void
+ {
+ $this->numObj++;
+ $this->o_embedded_file_dictionary(
+ $this->numObj,
+ 'new',
+ [
+ 'filepath' => $filepath,
+ 'filename' => $embeddedFilename,
+ 'description' => $description
+ ]
+ );
+ }
+
+ /**
+ * Add content to the documents info object
+ *
+ * @param string|array $label
+ * @param string $value
+ */
+ public function addInfo($label, string $value = ""): void
{
// this will only work if the label is one of the valid ones.
// modify this so that arrays can be passed as well.
@@ -3894,7 +5488,7 @@ EOT;
// else assume that they are both scalar, anything else will probably error
if (is_array($label)) {
foreach ($label as $l => $v) {
- $this->o_info($this->infoObject, $l, $v);
+ $this->o_info($this->infoObject, $l, (string) $v);
}
} else {
$this->o_info($this->infoObject, $label, $value);
@@ -3903,21 +5497,29 @@ EOT;
/**
* set the viewer preferences of the document, it is up to the browser to obey these.
+ *
+ * @param $label
+ * @param int $value
*/
function setPreferences($label, $value = 0)
{
// this will only work if the label is one of the valid ones.
if (is_array($label)) {
foreach ($label as $l => $v) {
- $this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
+ $this->o_catalog($this->catalogId, 'viewerPreferences', [$l => $v]);
}
} else {
- $this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
+ $this->o_catalog($this->catalogId, 'viewerPreferences', [$label => $value]);
}
}
/**
* extract an integer from a position in a byte stream
+ *
+ * @param $data
+ * @param $pos
+ * @param $num
+ * @return int
*/
private function getBytes(&$data, $pos, $num)
{
@@ -3934,6 +5536,9 @@ EOT;
/**
* Check if image already added to pdf image directory.
* If yes, need not to create again (pass empty data)
+ *
+ * @param string $imgname
+ * @return bool
*/
function image_iscached($imgname)
{
@@ -3944,19 +5549,20 @@ EOT;
* add a PNG image into the document, from a GD object
* this should work with remote files
*
- * @param string $file The PNG file
- * @param float $x X position
- * @param float $y Y position
- * @param float $w Width
- * @param float $h Height
- * @param resource $img A GD resource
- * @param bool $is_mask true if the image is a mask
- * @param bool $mask true if the image is masked
+ * @param \GdImage|resource $img A GD resource
+ * @param string $file The PNG file
+ * @param float $x X position
+ * @param float $y Y position
+ * @param float $w Width
+ * @param float $h Height
+ * @param bool $is_mask true if the image is a mask
+ * @param bool $mask true if the image is masked
+ * @throws Exception
*/
- function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null)
+ function addImagePng(&$img, $file, $x, $y, $w = 0.0, $h = 0.0, $is_mask = false, $mask = null)
{
if (!function_exists("imagepng")) {
- throw new Exception("The PHP GD extension is required, but is not installed.");
+ throw new \Exception("The PHP GD extension is required, but is not installed.");
}
//if already cached, need not to read again
@@ -3978,6 +5584,11 @@ EOT;
imagesavealpha($img, false/*!$is_mask && !$mask*/);
$error = 0;
+ //DEBUG_IMG_TEMP
+ //debugpng
+ if (defined("DEBUGPNG") && DEBUGPNG) {
+ print '[addImagePng ' . $file . ']';
+ }
ob_start();
@imagepng($img);
@@ -3986,6 +5597,11 @@ EOT;
if ($data == '') {
$error = 1;
$errormsg = 'trouble writing file from GD';
+ //DEBUG_IMG_TEMP
+ //debugpng
+ if (defined("DEBUGPNG") && DEBUGPNG) {
+ print 'trouble writing file from GD';
+ }
}
if ($error) {
@@ -3995,13 +5611,21 @@ EOT;
}
} //End isset($this->imagelist[$file]) (png Duplicate removal)
- $this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
+ $this->addPngFromBuf($data, $file, $x, $y, $w, $h, $is_mask, $mask);
}
+ /**
+ * @param $file
+ * @param $x
+ * @param $y
+ * @param $w
+ * @param $h
+ * @param $byte
+ */
protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte)
{
// generate images
- $img = imagecreatefrompng($file);
+ $img = @imagecreatefrompng($file);
if ($img === false) {
return;
@@ -4016,12 +5640,12 @@ EOT;
imagesavealpha($img, false);
// create temp alpha file
- $tempfile_alpha = tempnam($this->tmp, "cpdf_img_");
+ $tempfile_alpha = @tempnam($this->tmp, "cpdf_img_");
@unlink($tempfile_alpha);
$tempfile_alpha = "$tempfile_alpha.png";
// create temp plain file
- $tempfile_plain = tempnam($this->tmp, "cpdf_img_");
+ $tempfile_plain = @tempnam($this->tmp, "cpdf_img_");
@unlink($tempfile_plain);
$tempfile_plain = "$tempfile_plain.png";
@@ -4035,71 +5659,87 @@ EOT;
// Use PECL gmagick + Graphics Magic to process transparent PNG images
if (extension_loaded("gmagick")) {
- $gmagick = new Gmagick($file);
+ $gmagick = new \Gmagick($file);
$gmagick->setimageformat('png');
// Get opacity channel (negative of alpha channel)
$alpha_channel_neg = clone $gmagick;
- $alpha_channel_neg->separateimagechannel(Gmagick::CHANNEL_OPACITY);
+ $alpha_channel_neg->separateimagechannel(\Gmagick::CHANNEL_OPACITY);
// Negate opacity channel
- $alpha_channel = new Gmagick();
+ $alpha_channel = new \Gmagick();
$alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
- $alpha_channel->compositeimage($alpha_channel_neg, Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
- $alpha_channel->separateimagechannel(Gmagick::CHANNEL_RED);
+ $alpha_channel->compositeimage($alpha_channel_neg, \Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
+ $alpha_channel->separateimagechannel(\Gmagick::CHANNEL_RED);
$alpha_channel->writeimage($tempfile_alpha);
// Cast to 8bit+palette
- $imgalpha_ = imagecreatefrompng($tempfile_alpha);
+ $imgalpha_ = @imagecreatefrompng($tempfile_alpha);
imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
imagedestroy($imgalpha_);
imagepng($imgalpha, $tempfile_alpha);
// Make opaque image
- $color_channels = new Gmagick();
+ $color_channels = new \Gmagick();
$color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
- $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYRED, 0, 0);
- $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYGREEN, 0, 0);
- $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYBLUE, 0, 0);
+ $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYRED, 0, 0);
+ $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYGREEN, 0, 0);
+ $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYBLUE, 0, 0);
$color_channels->writeimage($tempfile_plain);
- $imgplain = imagecreatefrompng($tempfile_plain);
- } // Use PECL imagick + ImageMagic to process transparent PNG images
+ $imgplain = @imagecreatefrompng($tempfile_plain);
+ }
+ // Use PECL imagick + ImageMagic to process transparent PNG images
elseif (extension_loaded("imagick")) {
// Native cloning was added to pecl-imagick in svn commit 263814
// the first version containing it was 3.0.1RC1
static $imagickClonable = null;
if ($imagickClonable === null) {
- $imagickClonable = version_compare(phpversion('imagick'), '3.0.1rc1') > 0;
+ $imagickClonable = true;
+ if (defined('Imagick::IMAGICK_EXTVER')) {
+ $imagickVersion = \Imagick::IMAGICK_EXTVER;
+ } else {
+ $imagickVersion = '0';
+ }
+ if (version_compare($imagickVersion, '0.0.1', '>=')) {
+ $imagickClonable = version_compare($imagickVersion, '3.0.1rc1', '>=');
+ }
}
- $imagick = new Imagick($file);
+ $imagick = new \Imagick($file);
$imagick->setFormat('png');
// Get opacity channel (negative of alpha channel)
- $alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
- $alpha_channel->separateImageChannel(Imagick::CHANNEL_ALPHA);
- $alpha_channel->negateImage(true);
- $alpha_channel->writeImage($tempfile_alpha);
+ if ($imagick->getImageAlphaChannel()) {
+ $alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
+ $alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA);
+ // Since ImageMagick7 negate invert transparency as default
+ if (\Imagick::getVersion()['versionNumber'] < 1800) {
+ $alpha_channel->negateImage(true);
+ }
+ $alpha_channel->writeImage($tempfile_alpha);
- // Cast to 8bit+palette
- $imgalpha_ = imagecreatefrompng($tempfile_alpha);
- imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
- imagedestroy($imgalpha_);
- imagepng($imgalpha, $tempfile_alpha);
+ // Cast to 8bit+palette
+ $imgalpha_ = @imagecreatefrompng($tempfile_alpha);
+ imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
+ imagedestroy($imgalpha_);
+ imagepng($imgalpha, $tempfile_alpha);
+ } else {
+ $tempfile_alpha = null;
+ }
// Make opaque image
- $color_channels = new Imagick();
+ $color_channels = new \Imagick();
$color_channels->newImage($wpx, $hpx, "#FFFFFF", "png");
- $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYRED, 0, 0);
- $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYGREEN, 0, 0);
- $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYBLUE, 0, 0);
+ $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYRED, 0, 0);
+ $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYGREEN, 0, 0);
+ $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYBLUE, 0, 0);
$color_channels->writeImage($tempfile_plain);
- $imgplain = imagecreatefrompng($tempfile_plain);
+ $imgplain = @imagecreatefrompng($tempfile_plain);
} else {
// allocated colors cache
- $allocated_colors = array();
+ $allocated_colors = [];
// extract alpha channel
for ($xpx = 0; $xpx < $wpx; ++$xpx) {
@@ -4111,7 +5751,7 @@ EOT;
if ($eight_bit) {
// with gamma correction
$gammacorr = 2.2;
- $pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
+ $pixel = round(pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255);
} else {
// without gamma correction
$pixel = (127 - $alpha) * 2;
@@ -4141,27 +5781,49 @@ EOT;
imagepng($imgplain, $tempfile_plain);
}
+ $this->imageAlphaList[$file] = [$tempfile_alpha, $tempfile_plain];
+
// embed mask image
- $this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
- imagedestroy($imgalpha);
+ if ($tempfile_alpha) {
+ $this->addImagePng($imgalpha, $tempfile_alpha, $x, $y, $w, $h, true);
+ imagedestroy($imgalpha);
+ $this->imageCache[] = $tempfile_alpha;
+ }
// embed image, masked with previously embedded mask
- $this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, true);
+ $this->addImagePng($imgplain, $tempfile_plain, $x, $y, $w, $h, false, ($tempfile_alpha !== null));
imagedestroy($imgplain);
-
- // remove temp files
- unlink($tempfile_alpha);
- unlink($tempfile_plain);
+ $this->imageCache[] = $tempfile_plain;
}
/**
* add a PNG image into the document, from a file
* this should work with remote files
+ *
+ * @param $file
+ * @param $x
+ * @param $y
+ * @param int $w
+ * @param int $h
+ * @throws Exception
*/
function addPngFromFile($file, $x, $y, $w = 0, $h = 0)
{
if (!function_exists("imagecreatefrompng")) {
- throw new Exception("The PHP GD extension is required, but is not installed.");
+ throw new \Exception("The PHP GD extension is required, but is not installed.");
+ }
+
+ if (isset($this->imageAlphaList[$file])) {
+ [$alphaFile, $plainFile] = $this->imageAlphaList[$file];
+
+ if ($alphaFile) {
+ $img = null;
+ $this->addImagePng($img, $alphaFile, $x, $y, $w, $h, true);
+ }
+
+ $img = null;
+ $this->addImagePng($img, $plainFile, $x, $y, $w, $h, false, ($plainFile !== null));
+ return;
}
//if already cached, need not to read again
@@ -4177,10 +5839,11 @@ EOT;
// 3 => indexed
// 4 => greyscale with alpha
// 6 => fullcolor with alpha
- $is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
+ $is_alpha = in_array($color_type, [4, 6]) || ($color_type == 3 && $bit_depth != 4);
if ($is_alpha) { // exclude grayscale alpha
- return $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
+ $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
+ return;
}
//png files typically contain an alpha channel.
@@ -4216,7 +5879,7 @@ EOT;
imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy);
imagedestroy($imgtmp);
}
- $this->addImagePng($file, $x, $y, $w, $h, $img);
+ $this->addImagePng($img, $file, $x, $y, $w, $h);
if ($img) {
imagedestroy($img);
@@ -4224,9 +5887,44 @@ EOT;
}
/**
- * add a PNG image into the document, from a memory buffer of the file
+ * add a PNG image into the document, from a file
+ * this should work with remote files
+ *
+ * @param $file
+ * @param $x
+ * @param $y
+ * @param int $w
+ * @param int $h
*/
- function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null)
+ function addSvgFromFile($file, $x, $y, $w = 0, $h = 0)
+ {
+ $doc = new \Svg\Document();
+ $doc->loadFile($file);
+ $dimensions = $doc->getDimensions();
+
+ $this->save();
+
+ $this->transform([$w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y]);
+
+ $surface = new \Svg\Surface\SurfaceCpdf($doc, $this);
+ $doc->render($surface);
+
+ $this->restore();
+ }
+
+ /**
+ * add a PNG image into the document, from a memory buffer of the file
+ *
+ * @param $data
+ * @param $file
+ * @param $x
+ * @param $y
+ * @param float $w
+ * @param float $h
+ * @param bool $is_mask
+ * @param null $mask
+ */
+ function addPngFromBuf(&$data, $file, $x, $y, $w = 0.0, $h = 0.0, $is_mask = false, $mask = null)
{
if (isset($this->imagelist[$file])) {
$data = null;
@@ -4248,6 +5946,10 @@ EOT;
if (mb_substr($data, 0, 8, '8bit') != $header) {
$error = 1;
+ if (defined("DEBUGPNG") && DEBUGPNG) {
+ print '[addPngFromFile this file does not have a valid header ' . $file . ']';
+ }
+
$errormsg = 'this file does not have a valid header';
}
}
@@ -4259,7 +5961,7 @@ EOT;
// cycle through the file, identifying chunks
$haveHeader = 0;
- $info = array();
+ $info = [];
$idata = '';
$pdata = '';
@@ -4284,7 +5986,7 @@ EOT;
$error = 1;
//debugpng
- if (DEBUGPNG) {
+ if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile unsupported compression method ' . $file . ']';
}
@@ -4295,7 +5997,7 @@ EOT;
$error = 1;
//debugpng
- if (DEBUGPNG) {
+ if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile unsupported filter method ' . $file . ']';
}
@@ -4314,16 +6016,16 @@ EOT;
case 'tRNS':
//this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
//print "tRNS found, color type = ".$info['colorType']."\n";
- $transparency = array();
+ $transparency = [];
switch ($info['colorType']) {
// indexed color, rbg
case 3:
/* corresponding to entries in the plte chunk
- Alpha for palette index 0: 1 byte
- Alpha for palette index 1: 1 byte
- ...etc...
- */
+ Alpha for palette index 0: 1 byte
+ Alpha for palette index 1: 1 byte
+ ...etc...
+ */
// there will be one entry for each palette entry. up until the last non-opaque entry.
// set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
$transparency['type'] = 'indexed';
@@ -4341,8 +6043,8 @@ EOT;
// grayscale
case 0:
/* corresponding to entries in the plte chunk
- Gray: 2 bytes, range 0 .. (2^bitdepth)-1
- */
+ Gray: 2 bytes, range 0 .. (2^bitdepth)-1
+ */
// $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
$transparency['type'] = 'indexed';
$transparency['data'] = ord($data[$p + 8 + 1]);
@@ -4351,10 +6053,10 @@ EOT;
// truecolor
case 2:
/* corresponding to entries in the plte chunk
- Red: 2 bytes, range 0 .. (2^bitdepth)-1
- Green: 2 bytes, range 0 .. (2^bitdepth)-1
- Blue: 2 bytes, range 0 .. (2^bitdepth)-1
- */
+ Red: 2 bytes, range 0 .. (2^bitdepth)-1
+ Green: 2 bytes, range 0 .. (2^bitdepth)-1
+ Blue: 2 bytes, range 0 .. (2^bitdepth)-1
+ */
$transparency['r'] = $this->getBytes($data, $p + 8, 2);
// r from truecolor
$transparency['g'] = $this->getBytes($data, $p + 10, 2);
@@ -4367,7 +6069,7 @@ EOT;
//unsupported transparency type
default:
- if (DEBUGPNG) {
+ if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile unsupported transparency type ' . $file . ']';
}
break;
@@ -4387,7 +6089,7 @@ EOT;
$error = 1;
//debugpng
- if (DEBUGPNG) {
+ if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile information header is missing ' . $file . ']';
}
@@ -4398,7 +6100,7 @@ EOT;
$error = 1;
//debugpng
- if (DEBUGPNG) {
+ if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']';
}
@@ -4410,7 +6112,7 @@ EOT;
$error = 1;
//debugpng
- if (DEBUGPNG) {
+ if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']';
}
@@ -4438,11 +6140,11 @@ EOT;
$error = 1;
//debugpng
- if (DEBUGPNG) {
+ if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']';
}
- $errormsg = 'transparancey alpha channel not supported, transparency only supported for palette images.';
+ $errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.';
}
}
@@ -4460,7 +6162,7 @@ EOT;
$this->numObj++;
// $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
- $options = array(
+ $options = [
'label' => $label,
'data' => $idata,
'bitsPerComponent' => $info['bitDepth'],
@@ -4472,14 +6174,14 @@ EOT;
'ncolor' => $ncolor,
'masked' => $mask,
'isMask' => $is_mask
- );
+ ];
if (isset($transparency)) {
$options['transparency'] = $transparency;
}
$this->o_image($this->numObj, 'new', $options);
- $this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']);
+ $this->imagelist[$file] = ['label' => $label, 'w' => $info['width'], 'h' => $info['height']];
}
if ($is_mask) {
@@ -4504,6 +6206,12 @@ EOT;
/**
* add a JPEG image into the document, from a file
+ *
+ * @param $img
+ * @param $x
+ * @param $y
+ * @param int $w
+ * @param int $h
*/
function addJpegFromFile($img, $x, $y, $w = 0, $h = 0)
{
@@ -4545,22 +6253,31 @@ EOT;
$h = $w * $imageHeight / $imageWidth;
}
- $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
+ $this->addJpegImage_common($data, $img, $imageWidth, $imageHeight, $x, $y, $w, $h, $channels);
}
/**
* common code used by the two JPEG adding functions
+ * @param $data
+ * @param $imgname
+ * @param $imageWidth
+ * @param $imageHeight
+ * @param $x
+ * @param $y
+ * @param int $w
+ * @param int $h
+ * @param int $channels
*/
private function addJpegImage_common(
&$data,
+ $imgname,
+ $imageWidth,
+ $imageHeight,
$x,
$y,
$w = 0,
$h = 0,
- $imageWidth,
- $imageHeight,
- $channels = 3,
- $imgname
+ $channels = 3
) {
if ($this->image_iscached($imgname)) {
$label = $this->imagelist[$imgname]['label'];
@@ -4584,21 +6301,21 @@ EOT;
$this->o_image(
$this->numObj,
'new',
- array(
+ [
'label' => $label,
'data' => &$data,
'iw' => $imageWidth,
'ih' => $imageHeight,
'channels' => $channels
- )
+ ]
);
- $this->imagelist[$imgname] = array(
+ $this->imagelist[$imgname] = [
'label' => $label,
'w' => $imageWidth,
'h' => $imageHeight,
'c' => $channels
- );
+ ];
}
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
@@ -4606,11 +6323,16 @@ EOT;
/**
* specify where the document should open when it first starts
+ *
+ * @param $style
+ * @param int $a
+ * @param int $b
+ * @param int $c
*/
function openHere($style, $a = 0, $b = 0, $c = 0)
{
// this function will open the document at a specified page, in a specified style
- // the values for style, and the required paramters are:
+ // the values for style, and the required parameters are:
// 'XYZ' left, top, zoom
// 'Fit'
// 'FitH' top
@@ -4623,7 +6345,7 @@ EOT;
$this->o_destination(
$this->numObj,
'new',
- array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
+ ['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c]
);
$id = $this->catalogId;
$this->o_catalog($id, 'openHere', $this->numObj);
@@ -4633,8 +6355,6 @@ EOT;
* Add JavaScript code to the PDF document
*
* @param string $code
- *
- * @return void
*/
function addJavascript($code)
{
@@ -4643,6 +6363,12 @@ EOT;
/**
* create a labelled destination within the document
+ *
+ * @param $label
+ * @param $style
+ * @param int $a
+ * @param int $b
+ * @param int $c
*/
function addDestination($label, $style, $a = 0, $b = 0, $c = 0)
{
@@ -4653,7 +6379,7 @@ EOT;
$this->o_destination(
$this->numObj,
'new',
- array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
+ ['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c]
);
$id = $this->numObj;
@@ -4665,6 +6391,9 @@ EOT;
* define font families, this is used to initialize the font families for the default fonts
* and for the user to add new ones for their fonts. The default bahavious can be overridden should
* that be desired.
+ *
+ * @param $family
+ * @param string $options
*/
function setFontFamily($family, $options = '')
{
@@ -4674,28 +6403,28 @@ EOT;
// these font families will be used to enable bold and italic markers to be included
// within text streams. html forms will be used...
$this->fontFamilies['Helvetica.afm'] =
- array(
+ [
'b' => 'Helvetica-Bold.afm',
'i' => 'Helvetica-Oblique.afm',
'bi' => 'Helvetica-BoldOblique.afm',
'ib' => 'Helvetica-BoldOblique.afm'
- );
+ ];
$this->fontFamilies['Courier.afm'] =
- array(
+ [
'b' => 'Courier-Bold.afm',
'i' => 'Courier-Oblique.afm',
'bi' => 'Courier-BoldOblique.afm',
'ib' => 'Courier-BoldOblique.afm'
- );
+ ];
$this->fontFamilies['Times-Roman.afm'] =
- array(
+ [
'b' => 'Times-Bold.afm',
'i' => 'Times-Italic.afm',
'bi' => 'Times-BoldItalic.afm',
'ib' => 'Times-BoldItalic.afm'
- );
+ ];
}
} else {
@@ -4709,6 +6438,8 @@ EOT;
/**
* used to add messages for use in debugging
+ *
+ * @param $message
*/
function addMessage($message)
{
@@ -4717,6 +6448,8 @@ EOT;
/**
* a few functions which should allow the document to be treated transactionally.
+ *
+ * @param $action
*/
function transaction($action)
{
diff --git a/library/vendor/dompdf/lib/fonts/Courier-Bold.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-Bold.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Courier-Bold.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-Bold.afm
diff --git a/library/vendor/dompdf/lib/fonts/Courier-BoldOblique.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-BoldOblique.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Courier-BoldOblique.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-BoldOblique.afm
diff --git a/library/vendor/dompdf/lib/fonts/Courier-Oblique.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-Oblique.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Courier-Oblique.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-Oblique.afm
diff --git a/library/vendor/dompdf/lib/fonts/Courier.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Courier.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier.afm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-Bold.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Bold.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSans-Bold.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Bold.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-Bold.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Bold.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSans-Bold.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Bold.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-BoldOblique.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-BoldOblique.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSans-BoldOblique.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-BoldOblique.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-BoldOblique.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-BoldOblique.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSans-BoldOblique.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-BoldOblique.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-Oblique.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Oblique.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSans-Oblique.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Oblique.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-Oblique.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Oblique.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSans-Oblique.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Oblique.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSans.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSans.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-Bold.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Bold.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-Bold.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Bold.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-Bold.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Bold.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-Bold.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Bold.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-Oblique.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Oblique.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-Oblique.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Oblique.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-Oblique.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Oblique.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-Oblique.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Oblique.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-Bold.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Bold.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-Bold.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Bold.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-Bold.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Bold.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-Bold.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Bold.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-Italic.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Italic.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-Italic.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Italic.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-Italic.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Italic.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-Italic.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Italic.ufm
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif.ttf
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSerif.ttf
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif.ttf
diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif.ufm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/DejaVuSerif.ufm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif.ufm
diff --git a/library/vendor/dompdf/lib/fonts/Helvetica-Bold.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-Bold.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Helvetica-Bold.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-Bold.afm
diff --git a/library/vendor/dompdf/lib/fonts/Helvetica-BoldOblique.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-BoldOblique.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Helvetica-BoldOblique.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-BoldOblique.afm
diff --git a/library/vendor/dompdf/lib/fonts/Helvetica-Oblique.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-Oblique.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Helvetica-Oblique.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-Oblique.afm
diff --git a/library/vendor/dompdf/lib/fonts/Helvetica.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Helvetica.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica.afm
diff --git a/library/vendor/dompdf/lib/fonts/Symbol.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Symbol.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Symbol.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Symbol.afm
diff --git a/library/vendor/dompdf/lib/fonts/Times-Bold.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Bold.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Times-Bold.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Bold.afm
diff --git a/library/vendor/dompdf/lib/fonts/Times-BoldItalic.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-BoldItalic.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Times-BoldItalic.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-BoldItalic.afm
diff --git a/library/vendor/dompdf/lib/fonts/Times-Italic.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Italic.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Times-Italic.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Italic.afm
diff --git a/library/vendor/dompdf/lib/fonts/Times-Roman.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Roman.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/Times-Roman.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Roman.afm
diff --git a/library/vendor/dompdf/lib/fonts/ZapfDingbats.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/ZapfDingbats.afm
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/ZapfDingbats.afm
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/ZapfDingbats.afm
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json
new file mode 100644
index 000000000..c6abf158b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json
@@ -0,0 +1,80 @@
+{
+ "sans-serif": {
+ "normal": "Helvetica",
+ "bold": "Helvetica-Bold",
+ "italic": "Helvetica-Oblique",
+ "bold_italic": "Helvetica-BoldOblique"
+ },
+ "times": {
+ "normal": "Times-Roman",
+ "bold": "Times-Bold",
+ "italic": "Times-Italic",
+ "bold_italic": "Times-BoldItalic"
+ },
+ "times-roman": {
+ "normal": "Times-Roman",
+ "bold": "Times-Bold",
+ "italic": "Times-Italic",
+ "bold_italic": "Times-BoldItalic"
+ },
+ "courier": {
+ "normal": "Courier",
+ "bold": "Courier-Bold",
+ "italic": "Courier-Oblique",
+ "bold_italic": "Courier-BoldOblique"
+ },
+ "helvetica": {
+ "normal": "Helvetica",
+ "bold": "Helvetica-Bold",
+ "italic": "Helvetica-Oblique",
+ "bold_italic": "Helvetica-BoldOblique"
+ },
+ "zapfdingbats": {
+ "normal": "ZapfDingbats",
+ "bold": "ZapfDingbats",
+ "italic": "ZapfDingbats",
+ "bold_italic": "ZapfDingbats"
+ },
+ "symbol": {
+ "normal": "Symbol",
+ "bold": "Symbol",
+ "italic": "Symbol",
+ "bold_italic": "Symbol"
+ },
+ "serif": {
+ "normal": "Times-Roman",
+ "bold": "Times-Bold",
+ "italic": "Times-Italic",
+ "bold_italic": "Times-BoldItalic"
+ },
+ "monospace": {
+ "normal": "Courier",
+ "bold": "Courier-Bold",
+ "italic": "Courier-Oblique",
+ "bold_italic": "Courier-BoldOblique"
+ },
+ "fixed": {
+ "normal": "Courier",
+ "bold": "Courier-Bold",
+ "italic": "Courier-Oblique",
+ "bold_italic": "Courier-BoldOblique"
+ },
+ "dejavu sans": {
+ "bold": "DejaVuSans-Bold",
+ "bold_italic": "DejaVuSans-BoldOblique",
+ "italic": "DejaVuSans-Oblique",
+ "normal": "DejaVuSans"
+ },
+ "dejavu sans mono": {
+ "bold": "DejaVuSansMono-Bold",
+ "bold_italic": "DejaVuSansMono-BoldOblique",
+ "italic": "DejaVuSansMono-Oblique",
+ "normal": "DejaVuSansMono"
+ },
+ "dejavu serif": {
+ "bold": "DejaVuSerif-Bold",
+ "bold_italic": "DejaVuSerif-BoldItalic",
+ "italic": "DejaVuSerif-Italic",
+ "normal": "DejaVuSerif"
+ }
+}
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/fonts/mustRead.html b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/mustRead.html
similarity index 100%
rename from library/vendor/dompdf/lib/fonts/mustRead.html
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/mustRead.html
diff --git a/library/vendor/dompdf/lib/res/broken_image.png b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.png
similarity index 100%
rename from library/vendor/dompdf/lib/res/broken_image.png
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.png
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.svg b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.svg
new file mode 100644
index 000000000..83ba7e74c
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/res/html.css b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/html.css
similarity index 94%
rename from library/vendor/dompdf/lib/res/html.css
rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/html.css
index 372ff6e3c..89dcde633 100644
--- a/library/vendor/dompdf/lib/res/html.css
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/html.css
@@ -2,15 +2,12 @@
* dompdf default stylesheet.
*
* @package dompdf
- * @link http://dompdf.github.com/
- * @author Benj Carson
- * @author Blake Ross
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*
* Portions from Mozilla
* @link https://dxr.mozilla.org/mozilla-central/source/layout/style/res/html.css
- * @license http://mozilla.org/MPL/2.0/ Mozilla Public License, v. 2.0
+ * @license http://mozilla.org/MPL/2.0/ Mozilla Public License, v. 2.0
*
* Portions from W3C
* @link https://drafts.csswg.org/css-ui-3/#default-style-sheet
@@ -48,7 +45,7 @@ summary {
body {
page-break-before: avoid;
- display: block;
+ display: block !important;
counter-increment: page;
}
@@ -148,16 +145,18 @@ table {
}
table[border] {
- border-style: outset;
- border-color: gray;
+ border: outset gray;
}
-/* This won't work (???) */
-/*
table[border] td,
table[border] th {
- border: 1pt solid grey;
-}*/
+ border: 1px inset gray;
+}
+
+table[border="0"] td,
+table[border="0"] th {
+ border-width: 0;
+}
/* make sure backgrounds are inherited in tables -- see bug 4510 */
td, th, tr {
@@ -213,14 +212,12 @@ td {
th {
display: table-cell;
vertical-align: inherit;
+ text-align: center;
font-weight: bold;
padding: 1px;
}
/* inlines */
-q {
- quotes: '"' '"' "'" "'"; /* FIXME only the first level is used */
-}
q:before {
content: open-quote;
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/CPDF.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/CPDF.php
new file mode 100644
index 000000000..e8fc6ae4d
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/CPDF.php
@@ -0,0 +1,944 @@
+ [0.0, 0.0, 4767.87, 6740.79],
+ "2a0" => [0.0, 0.0, 3370.39, 4767.87],
+ "a0" => [0.0, 0.0, 2383.94, 3370.39],
+ "a1" => [0.0, 0.0, 1683.78, 2383.94],
+ "a2" => [0.0, 0.0, 1190.55, 1683.78],
+ "a3" => [0.0, 0.0, 841.89, 1190.55],
+ "a4" => [0.0, 0.0, 595.28, 841.89],
+ "a5" => [0.0, 0.0, 419.53, 595.28],
+ "a6" => [0.0, 0.0, 297.64, 419.53],
+ "a7" => [0.0, 0.0, 209.76, 297.64],
+ "a8" => [0.0, 0.0, 147.40, 209.76],
+ "a9" => [0.0, 0.0, 104.88, 147.40],
+ "a10" => [0.0, 0.0, 73.70, 104.88],
+ "b0" => [0.0, 0.0, 2834.65, 4008.19],
+ "b1" => [0.0, 0.0, 2004.09, 2834.65],
+ "b2" => [0.0, 0.0, 1417.32, 2004.09],
+ "b3" => [0.0, 0.0, 1000.63, 1417.32],
+ "b4" => [0.0, 0.0, 708.66, 1000.63],
+ "b5" => [0.0, 0.0, 498.90, 708.66],
+ "b6" => [0.0, 0.0, 354.33, 498.90],
+ "b7" => [0.0, 0.0, 249.45, 354.33],
+ "b8" => [0.0, 0.0, 175.75, 249.45],
+ "b9" => [0.0, 0.0, 124.72, 175.75],
+ "b10" => [0.0, 0.0, 87.87, 124.72],
+ "c0" => [0.0, 0.0, 2599.37, 3676.54],
+ "c1" => [0.0, 0.0, 1836.85, 2599.37],
+ "c2" => [0.0, 0.0, 1298.27, 1836.85],
+ "c3" => [0.0, 0.0, 918.43, 1298.27],
+ "c4" => [0.0, 0.0, 649.13, 918.43],
+ "c5" => [0.0, 0.0, 459.21, 649.13],
+ "c6" => [0.0, 0.0, 323.15, 459.21],
+ "c7" => [0.0, 0.0, 229.61, 323.15],
+ "c8" => [0.0, 0.0, 161.57, 229.61],
+ "c9" => [0.0, 0.0, 113.39, 161.57],
+ "c10" => [0.0, 0.0, 79.37, 113.39],
+ "ra0" => [0.0, 0.0, 2437.80, 3458.27],
+ "ra1" => [0.0, 0.0, 1729.13, 2437.80],
+ "ra2" => [0.0, 0.0, 1218.90, 1729.13],
+ "ra3" => [0.0, 0.0, 864.57, 1218.90],
+ "ra4" => [0.0, 0.0, 609.45, 864.57],
+ "sra0" => [0.0, 0.0, 2551.18, 3628.35],
+ "sra1" => [0.0, 0.0, 1814.17, 2551.18],
+ "sra2" => [0.0, 0.0, 1275.59, 1814.17],
+ "sra3" => [0.0, 0.0, 907.09, 1275.59],
+ "sra4" => [0.0, 0.0, 637.80, 907.09],
+ "letter" => [0.0, 0.0, 612.00, 792.00],
+ "half-letter" => [0.0, 0.0, 396.00, 612.00],
+ "legal" => [0.0, 0.0, 612.00, 1008.00],
+ "ledger" => [0.0, 0.0, 1224.00, 792.00],
+ "tabloid" => [0.0, 0.0, 792.00, 1224.00],
+ "executive" => [0.0, 0.0, 521.86, 756.00],
+ "folio" => [0.0, 0.0, 612.00, 936.00],
+ "commercial #10 envelope" => [0.0, 0.0, 684.00, 297.00],
+ "catalog #10 1/2 envelope" => [0.0, 0.0, 648.00, 864.00],
+ "8.5x11" => [0.0, 0.0, 612.00, 792.00],
+ "8.5x14" => [0.0, 0.0, 612.00, 1008.00],
+ "11x17" => [0.0, 0.0, 792.00, 1224.00],
+ ];
+
+ /**
+ * The Dompdf object
+ *
+ * @var Dompdf
+ */
+ protected $_dompdf;
+
+ /**
+ * Instance of Cpdf class
+ *
+ * @var \Dompdf\Cpdf
+ */
+ protected $_pdf;
+
+ /**
+ * PDF width, in points
+ *
+ * @var float
+ */
+ protected $_width;
+
+ /**
+ * PDF height, in points
+ *
+ * @var float
+ */
+ protected $_height;
+
+ /**
+ * Current page number
+ *
+ * @var int
+ */
+ protected $_page_number;
+
+ /**
+ * Total number of pages
+ *
+ * @var int
+ */
+ protected $_page_count;
+
+ /**
+ * Array of pages for accessing after rendering is initially complete
+ *
+ * @var array
+ */
+ protected $_pages;
+
+ /**
+ * Currently-applied opacity level (0 - 1)
+ *
+ * @var float
+ */
+ protected $_current_opacity = 1;
+
+ public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null)
+ {
+ if (is_array($paper)) {
+ $size = array_map("floatval", $paper);
+ } else {
+ $paper = strtolower($paper);
+ $size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"];
+ }
+
+ if (strtolower($orientation) === "landscape") {
+ [$size[2], $size[3]] = [$size[3], $size[2]];
+ }
+
+ if ($dompdf === null) {
+ $this->_dompdf = new Dompdf();
+ } else {
+ $this->_dompdf = $dompdf;
+ }
+
+ $this->_pdf = new \Dompdf\Cpdf(
+ $size,
+ true,
+ $this->_dompdf->getOptions()->getFontCache(),
+ $this->_dompdf->getOptions()->getTempDir()
+ );
+
+ $this->_pdf->addInfo("Producer", sprintf("%s + CPDF", $this->_dompdf->version));
+ $time = substr_replace(date('YmdHisO'), '\'', -2, 0) . '\'';
+ $this->_pdf->addInfo("CreationDate", "D:$time");
+ $this->_pdf->addInfo("ModDate", "D:$time");
+
+ $this->_width = $size[2] - $size[0];
+ $this->_height = $size[3] - $size[1];
+
+ $this->_page_number = $this->_page_count = 1;
+
+ $this->_pages = [$this->_pdf->getFirstPageId()];
+ }
+
+ public function get_dompdf()
+ {
+ return $this->_dompdf;
+ }
+
+ /**
+ * Returns the Cpdf instance
+ *
+ * @return \Dompdf\Cpdf
+ */
+ public function get_cpdf()
+ {
+ return $this->_pdf;
+ }
+
+ public function add_info(string $label, string $value): void
+ {
+ $this->_pdf->addInfo($label, $value);
+ }
+
+ /**
+ * Opens a new 'object'
+ *
+ * While an object is open, all drawing actions are recorded in the object,
+ * as opposed to being drawn on the current page. Objects can be added
+ * later to a specific page or to several pages.
+ *
+ * The return value is an integer ID for the new object.
+ *
+ * @see CPDF::close_object()
+ * @see CPDF::add_object()
+ *
+ * @return int
+ */
+ public function open_object()
+ {
+ $ret = $this->_pdf->openObject();
+ $this->_pdf->saveState();
+ return $ret;
+ }
+
+ /**
+ * Reopens an existing 'object'
+ *
+ * @see CPDF::open_object()
+ * @param int $object the ID of a previously opened object
+ */
+ public function reopen_object($object)
+ {
+ $this->_pdf->reopenObject($object);
+ $this->_pdf->saveState();
+ }
+
+ /**
+ * Closes the current 'object'
+ *
+ * @see CPDF::open_object()
+ */
+ public function close_object()
+ {
+ $this->_pdf->restoreState();
+ $this->_pdf->closeObject();
+ }
+
+ /**
+ * Adds a specified 'object' to the document
+ *
+ * $object int specifying an object created with {@link
+ * CPDF::open_object()}. $where can be one of:
+ * - 'add' add to current page only
+ * - 'all' add to every page from the current one onwards
+ * - 'odd' add to all odd numbered pages from now on
+ * - 'even' add to all even numbered pages from now on
+ * - 'next' add the object to the next page only
+ * - 'nextodd' add to all odd numbered pages from the next one
+ * - 'nexteven' add to all even numbered pages from the next one
+ *
+ * @see Cpdf::addObject()
+ *
+ * @param int $object
+ * @param string $where
+ */
+ public function add_object($object, $where = 'all')
+ {
+ $this->_pdf->addObject($object, $where);
+ }
+
+ /**
+ * Stops the specified 'object' from appearing in the document.
+ *
+ * The object will stop being displayed on the page following the current
+ * one.
+ *
+ * @param int $object
+ */
+ public function stop_object($object)
+ {
+ $this->_pdf->stopObject($object);
+ }
+
+ /**
+ * Serialize the pdf object's current state for retrieval later
+ */
+ public function serialize_object($id)
+ {
+ return $this->_pdf->serializeObject($id);
+ }
+
+ public function reopen_serialized_object($obj)
+ {
+ return $this->_pdf->restoreSerializedObject($obj);
+ }
+
+ //........................................................................
+
+ public function get_width()
+ {
+ return $this->_width;
+ }
+
+ public function get_height()
+ {
+ return $this->_height;
+ }
+
+ public function get_page_number()
+ {
+ return $this->_page_number;
+ }
+
+ public function get_page_count()
+ {
+ return $this->_page_count;
+ }
+
+ /**
+ * Sets the current page number
+ *
+ * @param int $num
+ */
+ public function set_page_number($num)
+ {
+ $this->_page_number = $num;
+ }
+
+ public function set_page_count($count)
+ {
+ $this->_page_count = $count;
+ }
+
+ /**
+ * Sets the stroke color
+ *
+ * See {@link Style::set_color()} for the format of the color array.
+ *
+ * @param array $color
+ */
+ protected function _set_stroke_color($color)
+ {
+ $this->_pdf->setStrokeColor($color);
+ $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
+ $alpha *= $this->_current_opacity;
+ $this->_set_line_transparency("Normal", $alpha);
+ }
+
+ /**
+ * Sets the fill colour
+ *
+ * See {@link Style::set_color()} for the format of the colour array.
+ *
+ * @param array $color
+ */
+ protected function _set_fill_color($color)
+ {
+ $this->_pdf->setColor($color);
+ $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
+ $alpha *= $this->_current_opacity;
+ $this->_set_fill_transparency("Normal", $alpha);
+ }
+
+ /**
+ * Sets line transparency
+ * @see Cpdf::setLineTransparency()
+ *
+ * Valid blend modes are (case-sensitive):
+ *
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
+ * ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
+ * Exclusion
+ *
+ * @param string $mode the blending mode to use
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
+ */
+ protected function _set_line_transparency($mode, $opacity)
+ {
+ $this->_pdf->setLineTransparency($mode, $opacity);
+ }
+
+ /**
+ * Sets fill transparency
+ * @see Cpdf::setFillTransparency()
+ *
+ * Valid blend modes are (case-sensitive):
+ *
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
+ * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
+ * Exclusion
+ *
+ * @param string $mode the blending mode to use
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
+ */
+ protected function _set_fill_transparency($mode, $opacity)
+ {
+ $this->_pdf->setFillTransparency($mode, $opacity);
+ }
+
+ /**
+ * Sets the line style
+ *
+ * @see Cpdf::setLineStyle()
+ *
+ * @param float $width
+ * @param string $cap
+ * @param string $join
+ * @param array $dash
+ */
+ protected function _set_line_style($width, $cap, $join, $dash)
+ {
+ $this->_pdf->setLineStyle($width, $cap, $join, $dash);
+ }
+
+ public function set_opacity(float $opacity, string $mode = "Normal"): void
+ {
+ $this->_set_line_transparency($mode, $opacity);
+ $this->_set_fill_transparency($mode, $opacity);
+ $this->_current_opacity = $opacity;
+ }
+
+ public function set_default_view($view, $options = [])
+ {
+ array_unshift($options, $view);
+ call_user_func_array([$this->_pdf, "openHere"], $options);
+ }
+
+ /**
+ * Remaps y coords from 4th to 1st quadrant
+ *
+ * @param float $y
+ * @return float
+ */
+ protected function y($y)
+ {
+ return $this->_height - $y;
+ }
+
+ public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
+ {
+ $this->_set_stroke_color($color);
+ $this->_set_line_style($width, $cap, "", $style);
+
+ $this->_pdf->line($x1, $this->y($y1),
+ $x2, $this->y($y2));
+ $this->_set_line_transparency("Normal", $this->_current_opacity);
+ }
+
+ public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
+ {
+ $this->_set_stroke_color($color);
+ $this->_set_line_style($width, $cap, "", $style);
+
+ $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
+ $this->_set_line_transparency("Normal", $this->_current_opacity);
+ }
+
+ public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
+ {
+ $this->_set_stroke_color($color);
+ $this->_set_line_style($width, $cap, "", $style);
+ $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
+ $this->_set_line_transparency("Normal", $this->_current_opacity);
+ }
+
+ public function filled_rectangle($x1, $y1, $w, $h, $color)
+ {
+ $this->_set_fill_color($color);
+ $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
+ $this->_set_fill_transparency("Normal", $this->_current_opacity);
+ }
+
+ public function clipping_rectangle($x1, $y1, $w, $h)
+ {
+ $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
+ }
+
+ public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
+ {
+ $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
+ }
+
+ public function clipping_polygon(array $points): void
+ {
+ // Adjust y values
+ for ($i = 1; $i < count($points); $i += 2) {
+ $points[$i] = $this->y($points[$i]);
+ }
+
+ $this->_pdf->clippingPolygon($points);
+ }
+
+ public function clipping_end()
+ {
+ $this->_pdf->clippingEnd();
+ }
+
+ public function save()
+ {
+ $this->_pdf->saveState();
+ }
+
+ public function restore()
+ {
+ $this->_pdf->restoreState();
+ }
+
+ public function rotate($angle, $x, $y)
+ {
+ $this->_pdf->rotate($angle, $x, $y);
+ }
+
+ public function skew($angle_x, $angle_y, $x, $y)
+ {
+ $this->_pdf->skew($angle_x, $angle_y, $x, $y);
+ }
+
+ public function scale($s_x, $s_y, $x, $y)
+ {
+ $this->_pdf->scale($s_x, $s_y, $x, $y);
+ }
+
+ public function translate($t_x, $t_y)
+ {
+ $this->_pdf->translate($t_x, $t_y);
+ }
+
+ public function transform($a, $b, $c, $d, $e, $f)
+ {
+ $this->_pdf->transform([$a, $b, $c, $d, $e, $f]);
+ }
+
+ public function polygon($points, $color, $width = null, $style = [], $fill = false)
+ {
+ $this->_set_fill_color($color);
+ $this->_set_stroke_color($color);
+
+ if (!$fill && isset($width)) {
+ $this->_set_line_style($width, "square", "miter", $style);
+ }
+
+ // Adjust y values
+ for ($i = 1; $i < count($points); $i += 2) {
+ $points[$i] = $this->y($points[$i]);
+ }
+
+ $this->_pdf->polygon($points, $fill);
+
+ $this->_set_fill_transparency("Normal", $this->_current_opacity);
+ $this->_set_line_transparency("Normal", $this->_current_opacity);
+ }
+
+ public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
+ {
+ $this->_set_fill_color($color);
+ $this->_set_stroke_color($color);
+
+ if (!$fill && isset($width)) {
+ $this->_set_line_style($width, "round", "round", $style);
+ }
+
+ $this->_pdf->ellipse($x, $this->y($y), $r, 0, 0, 8, 0, 360, 1, $fill);
+
+ $this->_set_fill_transparency("Normal", $this->_current_opacity);
+ $this->_set_line_transparency("Normal", $this->_current_opacity);
+ }
+
+ /**
+ * Convert image to a PNG image
+ *
+ * @param string $image_url
+ * @param string $type
+ *
+ * @return string|null The url of the newly converted image
+ */
+ protected function _convert_to_png($image_url, $type)
+ {
+ $filename = Cache::getTempImage($image_url);
+
+ if ($filename !== null && file_exists($filename)) {
+ return $filename;
+ }
+
+ $func_name = "imagecreatefrom$type";
+
+ set_error_handler([Helpers::class, "record_warnings"]);
+
+ if (!function_exists($func_name)) {
+ if (!method_exists(Helpers::class, $func_name)) {
+ throw new Exception("Function $func_name() not found. Cannot convert $type image: $image_url. Please install the image PHP extension.");
+ }
+ $func_name = [Helpers::class, $func_name];
+ }
+
+ try {
+ $im = call_user_func($func_name, $image_url);
+
+ if ($im) {
+ imageinterlace($im, false);
+
+ $tmp_dir = $this->_dompdf->getOptions()->getTempDir();
+ $tmp_name = @tempnam($tmp_dir, "{$type}_dompdf_img_");
+ @unlink($tmp_name);
+ $filename = "$tmp_name.png";
+
+ imagepng($im, $filename);
+ imagedestroy($im);
+ } else {
+ $filename = null;
+ }
+ } finally {
+ restore_error_handler();
+ }
+
+ if ($filename !== null) {
+ Cache::addTempImage($image_url, $filename);
+ }
+
+ return $filename;
+ }
+
+ public function image($img, $x, $y, $w, $h, $resolution = "normal")
+ {
+ [$width, $height, $type] = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
+
+ $debug_png = $this->_dompdf->getOptions()->getDebugPng();
+
+ if ($debug_png) {
+ print "[image:$img|$width|$height|$type]";
+ }
+
+ switch ($type) {
+ case "jpeg":
+ if ($debug_png) {
+ print '!!!jpg!!!';
+ }
+ $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
+ break;
+
+ case "webp":
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case "gif":
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case "bmp":
+ if ($debug_png) print "!!!{$type}!!!";
+ $img = $this->_convert_to_png($img, $type);
+ if ($img === null) {
+ if ($debug_png) print '!!!conversion to PDF failed!!!';
+ $this->image(Cache::$broken_image, $x, $y, $w, $h, $resolution);
+ break;
+ }
+
+ case "png":
+ if ($debug_png) print '!!!png!!!';
+
+ $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
+ break;
+
+ case "svg":
+ if ($debug_png) print '!!!SVG!!!';
+
+ $this->_pdf->addSvgFromFile($img, $x, $this->y($y) - $h, $w, $h);
+ break;
+
+ default:
+ if ($debug_png) print '!!!unknown!!!';
+ }
+ }
+
+ public function select($x, $y, $w, $h, $font, $size, $color = [0, 0, 0], $opts = [])
+ {
+ $pdf = $this->_pdf;
+
+ $font .= ".afm";
+ $pdf->selectFont($font);
+
+ if (!isset($pdf->acroFormId)) {
+ $pdf->addForm();
+ }
+
+ $ft = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE;
+ $ff = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE_COMBO;
+
+ $id = $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
+ $pdf->setFormFieldOpt($id, $opts);
+ }
+
+ public function textarea($x, $y, $w, $h, $font, $size, $color = [0, 0, 0])
+ {
+ $pdf = $this->_pdf;
+
+ $font .= ".afm";
+ $pdf->selectFont($font);
+
+ if (!isset($pdf->acroFormId)) {
+ $pdf->addForm();
+ }
+
+ $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
+ $ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_MULTILINE;
+
+ $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
+ }
+
+ public function input($x, $y, $w, $h, $type, $font, $size, $color = [0, 0, 0])
+ {
+ $pdf = $this->_pdf;
+
+ $font .= ".afm";
+ $pdf->selectFont($font);
+
+ if (!isset($pdf->acroFormId)) {
+ $pdf->addForm();
+ }
+
+ $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
+ $ff = 0;
+
+ switch ($type) {
+ case 'text':
+ $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
+ break;
+ case 'password':
+ $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
+ $ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_PASSWORD;
+ break;
+ case 'submit':
+ $ft = \Dompdf\Cpdf::ACROFORM_FIELD_BUTTON;
+ break;
+ }
+
+ $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
+ }
+
+ public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
+ {
+ $pdf = $this->_pdf;
+
+ $this->_set_fill_color($color);
+
+ $is_font_subsetting = $this->_dompdf->getOptions()->getIsFontSubsettingEnabled();
+ $pdf->selectFont($font . '.afm', '', true, $is_font_subsetting);
+
+ $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
+
+ $this->_set_fill_transparency("Normal", $this->_current_opacity);
+ }
+
+ public function javascript($code)
+ {
+ $this->_pdf->addJavascript($code);
+ }
+
+ //........................................................................
+
+ public function add_named_dest($anchorname)
+ {
+ $this->_pdf->addDestination($anchorname, "Fit");
+ }
+
+ public function add_link($url, $x, $y, $width, $height)
+ {
+ $y = $this->y($y) - $height;
+
+ if (strpos($url, '#') === 0) {
+ // Local link
+ $name = substr($url, 1);
+ if ($name) {
+ $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
+ }
+ } else {
+ $this->_pdf->addLink($url, $x, $y, $x + $width, $y + $height);
+ }
+ }
+
+ /**
+ * @throws FontNotFoundException
+ */
+ public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
+ {
+ $this->_pdf->selectFont($font, '', true, $this->_dompdf->getOptions()->getIsFontSubsettingEnabled());
+ return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
+ }
+
+ /**
+ * @throws FontNotFoundException
+ */
+ public function get_font_height($font, $size)
+ {
+ $options = $this->_dompdf->getOptions();
+ $this->_pdf->selectFont($font, '', true, $options->getIsFontSubsettingEnabled());
+
+ return $this->_pdf->getFontHeight($size) * $options->getFontHeightRatio();
+ }
+
+ /*function get_font_x_height($font, $size) {
+ $this->_pdf->selectFont($font);
+ $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
+ return $this->_pdf->getFontXHeight($size) * $ratio;
+ }*/
+
+ /**
+ * @throws FontNotFoundException
+ */
+ public function get_font_baseline($font, $size)
+ {
+ $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
+ return $this->get_font_height($font, $size) / $ratio;
+ }
+
+ /**
+ * Processes a callback or script on every page.
+ *
+ * The callback function receives the four parameters `int $pageNumber`,
+ * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
+ * that order. If a script is passed as string, the variables `$PAGE_NUM`,
+ * `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing
+ * a script as string is deprecated and will be removed in a future version.
+ *
+ * This function can be used to add page numbers to all pages after the
+ * first one, for example.
+ *
+ * @param callable|string $callback The callback function or PHP script to process on every page
+ */
+ public function page_script($callback): void
+ {
+ if (is_string($callback)) {
+ $this->processPageScript(function (
+ int $PAGE_NUM,
+ int $PAGE_COUNT,
+ self $pdf,
+ FontMetrics $fontMetrics
+ ) use ($callback) {
+ eval($callback);
+ });
+ return;
+ }
+
+ $this->processPageScript($callback);
+ }
+
+ public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
+ {
+ $this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) {
+ $text = str_replace(
+ ["{PAGE_NUM}", "{PAGE_COUNT}"],
+ [$pageNumber, $pageCount],
+ $text
+ );
+ $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
+ });
+ }
+
+ public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
+ {
+ $this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) {
+ $this->line($x1, $y1, $x2, $y2, $color, $width, $style);
+ });
+ }
+
+ /**
+ * @return int
+ */
+ public function new_page()
+ {
+ $this->_page_number++;
+ $this->_page_count++;
+
+ $ret = $this->_pdf->newPage();
+ $this->_pages[] = $ret;
+ return $ret;
+ }
+
+ protected function processPageScript(callable $callback): void
+ {
+ $pageNumber = 1;
+
+ foreach ($this->_pages as $pid) {
+ $this->reopen_object($pid);
+
+ $fontMetrics = $this->_dompdf->getFontMetrics();
+ $callback($pageNumber, $this->_page_count, $this, $fontMetrics);
+
+ $this->close_object();
+ $pageNumber++;
+ }
+ }
+
+ public function stream($filename = "document.pdf", $options = [])
+ {
+ if (headers_sent()) {
+ die("Unable to stream pdf: headers already sent");
+ }
+
+ if (!isset($options["compress"])) $options["compress"] = true;
+ if (!isset($options["Attachment"])) $options["Attachment"] = true;
+
+ $debug = !$options['compress'];
+ $tmp = ltrim($this->_pdf->output($debug));
+
+ header("Cache-Control: private");
+ header("Content-Type: application/pdf");
+ header("Content-Length: " . mb_strlen($tmp, "8bit"));
+
+ $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf";
+ $attachment = $options["Attachment"] ? "attachment" : "inline";
+ header(Helpers::buildContentDispositionHeader($attachment, $filename));
+
+ echo $tmp;
+ flush();
+ }
+
+ public function output($options = [])
+ {
+ if (!isset($options["compress"])) $options["compress"] = true;
+
+ $debug = !$options['compress'];
+
+ return $this->_pdf->output($debug);
+ }
+
+ /**
+ * Returns logging messages generated by the Cpdf class
+ *
+ * @return string
+ */
+ public function get_messages()
+ {
+ return $this->_pdf->messages;
+ }
+}
diff --git a/library/vendor/dompdf/src/Adapter/GD.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/GD.php
similarity index 58%
rename from library/vendor/dompdf/src/Adapter/GD.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/GD.php
index 92fbdbe11..8c10e4766 100644
--- a/library/vendor/dompdf/src/Adapter/GD.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/GD.php
@@ -1,17 +1,15 @@
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\Adapter;
use Dompdf\Canvas;
use Dompdf\Dompdf;
-use Dompdf\Image\Cache;
use Dompdf\Helpers;
+use Dompdf\Image\Cache;
/**
* Image rendering interface
@@ -26,98 +24,98 @@ class GD implements Canvas
/**
* @var Dompdf
*/
- private $_dompdf;
+ protected $_dompdf;
/**
* Resource handle for the image
*
- * @var resource
+ * @var \GdImage|resource
*/
- private $_img;
+ protected $_img;
/**
* Resource handle for the image
*
- * @var resource[]
+ * @var \GdImage[]|resource[]
*/
- private $_imgs;
+ protected $_imgs;
/**
* Apparent canvas width in pixels
*
* @var int
*/
- private $_width;
+ protected $_width;
/**
* Apparent canvas height in pixels
*
* @var int
*/
- private $_height;
+ protected $_height;
/**
* Actual image width in pixels
*
* @var int
*/
- private $_actual_width;
+ protected $_actual_width;
/**
* Actual image height in pixels
*
* @var int
*/
- private $_actual_height;
+ protected $_actual_height;
/**
* Current page number
*
* @var int
*/
- private $_page_number;
+ protected $_page_number;
/**
* Total number of pages
*
* @var int
*/
- private $_page_count;
+ protected $_page_count;
/**
* Image antialias factor
*
* @var float
*/
- private $_aa_factor;
+ protected $_aa_factor;
/**
* Allocated colors
*
* @var array
*/
- private $_colors;
+ protected $_colors;
/**
* Background color
*
* @var int
*/
- private $_bg_color;
+ protected $_bg_color;
/**
* Background color array
*
* @var int
*/
- private $_bg_color_array;
+ protected $_bg_color_array;
/**
* Actual DPI
*
* @var int
*/
- private $dpi;
+ protected $dpi;
/**
* Amount to scale font sizes
@@ -130,32 +128,31 @@ class GD implements Canvas
const FONT_SCALE = 0.75;
/**
- * Class constructor
- *
- * @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc.
- * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
- * @param Dompdf $dompdf
- * @param float $aa_factor Anti-aliasing factor, 1 for no AA
- * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
+ * @param string|float[] $paper The paper size to use as either a standard paper size (see {@link CPDF::$PAPER_SIZES}) or
+ * an array of the form `[x1, y1, x2, y2]` (typically `[0, 0, width, height]`).
+ * @param string $orientation The paper orientation, either `portrait` or `landscape`.
+ * @param Dompdf $dompdf The Dompdf instance.
+ * @param float $aa_factor Anti-aliasing factor, 1 for no AA
+ * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
*/
- public function __construct($size = 'letter', $orientation = "portrait", Dompdf $dompdf, $aa_factor = 1.0, $bg_color = array(1, 1, 1, 0))
+ public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null, $aa_factor = 1.0, $bg_color = [1, 1, 1, 0])
{
-
- if (!is_array($size)) {
- $size = strtolower($size);
-
- if (isset(CPDF::$PAPER_SIZES[$size])) {
- $size = CPDF::$PAPER_SIZES[$size];
- } else {
- $size = CPDF::$PAPER_SIZES["letter"];
- }
+ if (is_array($paper)) {
+ $size = array_map("floatval", $paper);
+ } else {
+ $paper = strtolower($paper);
+ $size = CPDF::$PAPER_SIZES[$paper] ?? CPDF::$PAPER_SIZES["letter"];
}
if (strtolower($orientation) === "landscape") {
- list($size[2], $size[3]) = array($size[3], $size[2]);
+ [$size[2], $size[3]] = [$size[3], $size[2]];
}
- $this->_dompdf = $dompdf;
+ if ($dompdf === null) {
+ $this->_dompdf = new Dompdf();
+ } else {
+ $this->_dompdf = $dompdf;
+ }
$this->dpi = $this->get_dompdf()->getOptions()->getDpi();
@@ -174,9 +171,11 @@ class GD implements Canvas
$this->_actual_width = $this->_upscale($this->_width);
$this->_actual_height = $this->_upscale($this->_height);
+ $this->_page_number = $this->_page_count = 0;
+
if (is_null($bg_color) || !is_array($bg_color)) {
// Pure white bg
- $bg_color = array(1, 1, 1, 0);
+ $bg_color = [1, 1, 1, 0];
}
$this->_bg_color_array = $bg_color;
@@ -184,18 +183,15 @@ class GD implements Canvas
$this->new_page();
}
- /**
- * @return Dompdf
- */
public function get_dompdf()
{
return $this->_dompdf;
}
/**
- * Return the GF image resource
+ * Return the GD image resource
*
- * @return resource
+ * @return \GdImage|resource
*/
public function get_image()
{
@@ -205,36 +201,28 @@ class GD implements Canvas
/**
* Return the image's width in pixels
*
- * @return float
+ * @return int
*/
public function get_width()
{
- return $this->_width / $this->_aa_factor;
+ return round($this->_width / $this->_aa_factor);
}
/**
* Return the image's height in pixels
*
- * @return float
+ * @return int
*/
public function get_height()
{
- return $this->_height / $this->_aa_factor;
+ return round($this->_height / $this->_aa_factor);
}
- /**
- * Returns the current page number
- * @return int
- */
public function get_page_number()
{
return $this->_page_number;
}
- /**
- * Returns the total number of pages in the document
- * @return int
- */
public function get_page_count()
{
return $this->_page_count;
@@ -250,23 +238,12 @@ class GD implements Canvas
$this->_page_number = $num;
}
- /**
- * Sets the page count
- *
- * @param int $count
- */
public function set_page_count($count)
{
$this->_page_count = $count;
}
- /**
- * Sets the opacity
- *
- * @param $opacity
- * @param $mode
- */
- public function set_opacity($opacity, $mode = "Normal")
+ public function set_opacity(float $opacity, string $mode = "Normal"): void
{
// FIXME
}
@@ -276,9 +253,9 @@ class GD implements Canvas
* previously allocated colors in $this->_colors.
*
* @param array $color The new current color
- * @return int The allocated color
+ * @return int The allocated color
*/
- private function _allocate_color($color)
+ protected function _allocate_color($color)
{
$a = isset($color["alpha"]) ? $color["alpha"] : 1;
@@ -288,10 +265,10 @@ class GD implements Canvas
list($r, $g, $b) = $color;
- $r *= 255;
- $g *= 255;
- $b *= 255;
- $a = 127 - ($a * 127);
+ $r = round($r * 255);
+ $g = round($g * 255);
+ $b = round($b * 255);
+ $a = round(127 - ($a * 127));
// Clip values
$r = $r > 255 ? 255 : $r;
@@ -323,11 +300,11 @@ class GD implements Canvas
* Scales value up to the current canvas DPI from 72 DPI
*
* @param float $length
- * @return float
+ * @return int
*/
- private function _upscale($length)
+ protected function _upscale($length)
{
- return ($length * $this->dpi) / 72 * $this->_aa_factor;
+ return round(($length * $this->dpi) / 72 * $this->_aa_factor);
}
/**
@@ -336,28 +313,59 @@ class GD implements Canvas
* @param float $length
* @return float
*/
- private function _downscale($length)
+ protected function _downscale($length)
{
- return ($length / $this->dpi * 72) / $this->_aa_factor;
+ return round(($length / $this->dpi * 72) / $this->_aa_factor);
}
- /**
- * Draws a line from x1,y1 to x2,y2
- *
- * See {@link Style::munge_color()} for the format of the color array.
- * See {@link Cpdf::setLineStyle()} for a description of the format of the
- * $style parameter (aka dash).
- *
- * @param float $x1
- * @param float $y1
- * @param float $x2
- * @param float $y2
- * @param array $color
- * @param float $width
- * @param array $style
- */
- public function line($x1, $y1, $x2, $y2, $color, $width, $style = null)
+ protected function convertStyle(array $style, int $color, int $width): array
{
+ $gdStyle = [];
+
+ if (count($style) === 1) {
+ $style[] = $style[0];
+ }
+
+ foreach ($style as $index => $s) {
+ $d = $this->_upscale($s);
+
+ for ($i = 0; $i < $d; $i++) {
+ for ($j = 0; $j < $width; $j++) {
+ $gdStyle[] = $index % 2 === 0
+ ? $color
+ : IMG_COLOR_TRANSPARENT;
+ }
+ }
+ }
+
+ return $gdStyle;
+ }
+
+ public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
+ {
+ // Account for the fact that round and square caps are expected to
+ // extend outwards
+ if ($cap === "round" || $cap === "square") {
+ // Shift line by half width
+ $w = $width / 2;
+ $a = $x2 - $x1;
+ $b = $y2 - $y1;
+ $c = sqrt($a ** 2 + $b ** 2);
+ $dx = $a * $w / $c;
+ $dy = $b * $w / $c;
+
+ $x1 -= $dx;
+ $x2 -= $dx;
+ $y1 -= $dy;
+ $y2 -= $dy;
+
+ // Adapt dash pattern
+ if (is_array($style)) {
+ foreach ($style as $index => &$s) {
+ $s = $index % 2 === 0 ? $s + $width : $s - $width;
+ }
+ }
+ }
// Scale by the AA factor and DPI
$x1 = $this->_upscale($x1);
@@ -370,34 +378,7 @@ class GD implements Canvas
// Convert the style array if required
if (is_array($style) && count($style) > 0) {
- $gd_style = array();
-
- if (count($style) == 1) {
- for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
- $gd_style[] = $c;
- }
-
- for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
- $gd_style[] = $this->_bg_color;
- }
- } else {
- $i = 0;
- foreach ($style as $length) {
- if ($i % 2 == 0) {
- // 'On' pattern
- for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
- $gd_style[] = $c;
- }
-
- } else {
- // Off pattern
- for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
- $gd_style[] = $this->_bg_color;
- }
- }
- $i++;
- }
- }
+ $gd_style = $this->convertStyle($style, $c, $width);
if (!empty($gd_style)) {
imagesetstyle($this->get_image(), $gd_style);
@@ -410,39 +391,59 @@ class GD implements Canvas
imageline($this->get_image(), $x1, $y1, $x2, $y2, $c);
}
- /**
- * @param float $x1
- * @param float $y1
- * @param float $r1
- * @param float $r2
- * @param float $astart
- * @param float $aend
- * @param array $color
- * @param float $width
- * @param array $style
- */
- public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array())
+ public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
{
- // @todo
+ // Account for the fact that round and square caps are expected to
+ // extend outwards
+ if ($cap === "round" || $cap === "square") {
+ // Adapt dash pattern
+ if (is_array($style)) {
+ foreach ($style as $index => &$s) {
+ $s = $index % 2 === 0 ? $s + $width : $s - $width;
+ }
+ }
+ }
+
+ // Scale by the AA factor and DPI
+ $x = $this->_upscale($x);
+ $y = $this->_upscale($y);
+ $w = $this->_upscale($r1 * 2);
+ $h = $this->_upscale($r2 * 2);
+ $width = $this->_upscale($width);
+
+ // Adapt angles as imagearc counts clockwise
+ $start = 360 - $aend;
+ $end = 360 - $astart;
+
+ $c = $this->_allocate_color($color);
+
+ // Convert the style array if required
+ if (is_array($style) && count($style) > 0) {
+ $gd_style = $this->convertStyle($style, $c, $width);
+
+ if (!empty($gd_style)) {
+ imagesetstyle($this->get_image(), $gd_style);
+ $c = IMG_COLOR_STYLED;
+ }
+ }
+
+ imagesetthickness($this->get_image(), $width);
+
+ imagearc($this->get_image(), $x, $y, $w, $h, $start, $end, $c);
}
- /**
- * Draws a rectangle at x1,y1 with width w and height h
- *
- * See {@link Style::munge_color()} for the format of the color array.
- * See {@link Cpdf::setLineStyle()} for a description of the $style
- * parameter (aka dash)
- *
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param array $color
- * @param float $width
- * @param array $style
- */
- public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null)
+ public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
{
+ // Account for the fact that round and square caps are expected to
+ // extend outwards
+ if ($cap === "round" || $cap === "square") {
+ // Adapt dash pattern
+ if (is_array($style)) {
+ foreach ($style as $index => &$s) {
+ $s = $index % 2 === 0 ? $s + $width : $s - $width;
+ }
+ }
+ }
// Scale by the AA factor and DPI
$x1 = $this->_upscale($x1);
@@ -455,13 +456,7 @@ class GD implements Canvas
// Convert the style array if required
if (is_array($style) && count($style) > 0) {
- $gd_style = array();
-
- foreach ($style as $length) {
- for ($i = 0; $i < $length; $i++) {
- $gd_style[] = $c;
- }
- }
+ $gd_style = $this->convertStyle($style, $c, $width);
if (!empty($gd_style)) {
imagesetstyle($this->get_image(), $gd_style);
@@ -471,20 +466,18 @@ class GD implements Canvas
imagesetthickness($this->get_image(), $width);
- imagerectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
+ if ($c === IMG_COLOR_STYLED) {
+ imagepolygon($this->get_image(), [
+ $x1, $y1,
+ $x1 + $w, $y1,
+ $x1 + $w, $y1 + $h,
+ $x1, $y1 + $h
+ ], $c);
+ } else {
+ imagerectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
+ }
}
- /**
- * Draws a filled rectangle at x1,y1 with width w and height h
- *
- * See {@link Style::munge_color()} for the format of the color array.
- *
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param array $color
- */
public function filled_rectangle($x1, $y1, $w, $h, $color)
{
// Scale by the AA factor and DPI
@@ -498,14 +491,6 @@ class GD implements Canvas
imagefilledrectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
}
- /**
- * Starts a clipping rectangle at x1,y1 with width w and height h
- *
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- */
public function clipping_rectangle($x1, $y1, $w, $h)
{
// @todo
@@ -516,127 +501,65 @@ class GD implements Canvas
// @todo
}
- /**
- * Ends the last clipping shape
- */
+ public function clipping_polygon(array $points): void
+ {
+ // @todo
+ }
+
public function clipping_end()
{
// @todo
}
- /**
- *
- */
public function save()
{
$this->get_dompdf()->getOptions()->setDpi(72);
}
- /**
- *
- */
public function restore()
{
$this->get_dompdf()->getOptions()->setDpi($this->dpi);
}
- /**
- * @param $angle
- * @param $x
- * @param $y
- */
public function rotate($angle, $x, $y)
{
// @todo
}
- /**
- * @param $angle_x
- * @param $angle_y
- * @param $x
- * @param $y
- */
public function skew($angle_x, $angle_y, $x, $y)
{
// @todo
}
- /**
- * @param $s_x
- * @param $s_y
- * @param $x
- * @param $y
- */
public function scale($s_x, $s_y, $x, $y)
{
// @todo
}
- /**
- * @param $t_x
- * @param $t_y
- */
public function translate($t_x, $t_y)
{
// @todo
}
- /**
- * @param $a
- * @param $b
- * @param $c
- * @param $d
- * @param $e
- * @param $f
- */
public function transform($a, $b, $c, $d, $e, $f)
{
// @todo
}
- /**
- * Draws a polygon
- *
- * The polygon is formed by joining all the points stored in the $points
- * array. $points has the following structure:
- *
- * array(0 => x1,
- * 1 => y1,
- * 2 => x2,
- * 3 => y2,
- * ...
- * );
- *
- *
- * See {@link Style::munge_color()} for the format of the color array.
- * See {@link Cpdf::setLineStyle()} for a description of the $style
- * parameter (aka dash)
- *
- * @param array $points
- * @param array $color
- * @param float $width
- * @param array $style
- * @param bool $fill Fills the polygon if true
- */
- public function polygon($points, $color, $width = null, $style = null, $fill = false)
+ public function polygon($points, $color, $width = null, $style = [], $fill = false)
{
-
// Scale each point by the AA factor and DPI
foreach (array_keys($points) as $i) {
$points[$i] = $this->_upscale($points[$i]);
}
+ $width = isset($width) ? $this->_upscale($width) : null;
+
$c = $this->_allocate_color($color);
// Convert the style array if required
- if (is_array($style) && count($style) > 0 && !$fill) {
- $gd_style = array();
-
- foreach ($style as $length) {
- for ($i = 0; $i < $length; $i++) {
- $gd_style[] = $c;
- }
- }
+ if (is_array($style) && count($style) > 0 && isset($width) && !$fill) {
+ $gd_style = $this->convertStyle($style, $c, $width);
if (!empty($gd_style)) {
imagesetstyle($this->get_image(), $gd_style);
@@ -644,48 +567,28 @@ class GD implements Canvas
}
}
- imagesetthickness($this->get_image(), $width);
+ imagesetthickness($this->get_image(), isset($width) ? $width : 0);
if ($fill) {
- imagefilledpolygon($this->get_image(), $points, count($points) / 2, $c);
+ imagefilledpolygon($this->get_image(), $points, $c);
} else {
- imagepolygon($this->get_image(), $points, count($points) / 2, $c);
+ imagepolygon($this->get_image(), $points, $c);
}
}
- /**
- * Draws a circle at $x,$y with radius $r
- *
- * See {@link Style::munge_color()} for the format of the color array.
- * See {@link Cpdf::setLineStyle()} for a description of the $style
- * parameter (aka dash)
- *
- * @param float $x
- * @param float $y
- * @param float $r
- * @param array $color
- * @param float $width
- * @param array $style
- * @param bool $fill Fills the circle if true
- */
- public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false)
+ public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
{
// Scale by the AA factor and DPI
$x = $this->_upscale($x);
$y = $this->_upscale($y);
- $r = $this->_upscale($r);
+ $d = $this->_upscale(2 * $r);
+ $width = isset($width) ? $this->_upscale($width) : null;
$c = $this->_allocate_color($color);
// Convert the style array if required
- if (is_array($style) && count($style) > 0 && !$fill) {
- $gd_style = array();
-
- foreach ($style as $length) {
- for ($i = 0; $i < $length; $i++) {
- $gd_style[] = $c;
- }
- }
+ if (is_array($style) && count($style) > 0 && isset($width) && !$fill) {
+ $gd_style = $this->convertStyle($style, $c, $width);
if (!empty($gd_style)) {
imagesetstyle($this->get_image(), $gd_style);
@@ -693,34 +596,21 @@ class GD implements Canvas
}
}
- imagesetthickness($this->get_image(), $width);
+ imagesetthickness($this->get_image(), isset($width) ? $width : 0);
if ($fill) {
- imagefilledellipse($this->get_image(), $x, $y, $r, $r, $c);
+ imagefilledellipse($this->get_image(), $x, $y, $d, $d, $c);
} else {
- imageellipse($this->get_image(), $x, $y, $r, $r, $c);
+ imageellipse($this->get_image(), $x, $y, $d, $d, $c);
}
}
/**
- * Add an image to the pdf.
- * The image is placed at the specified x and y coordinates with the
- * given width and height.
- *
- * @param string $img_url the path to the image
- * @param float $x x position
- * @param float $y y position
- * @param int $w width (in pixels)
- * @param int $h height (in pixels)
- * @param string $resolution
- * @return void
- *
* @throws \Exception
- * @internal param string $img_type the type (e.g. extension) of the image
*/
- public function image($img_url, $x, $y, $w, $h, $resolution = "normal")
+ public function image($img, $x, $y, $w, $h, $resolution = "normal")
{
- $img_type = Cache::detect_type($img_url, $this->get_dompdf()->getHttpContext());
+ $img_type = Cache::detect_type($img, $this->get_dompdf()->getHttpContext());
if (!$img_type) {
return;
@@ -728,12 +618,12 @@ class GD implements Canvas
$func_name = "imagecreatefrom$img_type";
if (!function_exists($func_name)) {
- if (!method_exists("Dompdf\Helpers", $func_name)) {
- throw new \Exception("Function $func_name() not found. Cannot convert $img_type image: $img_url. Please install the image PHP extension.");
+ if (!method_exists(Helpers::class, $func_name)) {
+ throw new \Exception("Function $func_name() not found. Cannot convert $img_type image: $img. Please install the image PHP extension.");
}
- $func_name = "\\Dompdf\\Helpers::" . $func_name;
+ $func_name = [Helpers::class, $func_name];
}
- $src = @call_user_func($func_name, $img_url);
+ $src = @call_user_func($func_name, $img);
if (!$src) {
return; // Probably should add to $_dompdf_errors or whatever here
@@ -752,30 +642,14 @@ class GD implements Canvas
imagecopyresampled($this->get_image(), $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
}
- /**
- * Writes text at the specified x and y coordinates
- * See {@link Style::munge_color()} for the format of the color array.
- *
- * @param float $x
- * @param float $y
- * @param string $text the text to write
- * @param string $font the font file to use
- * @param float $size the font size, in points
- * @param array $color
- * @param float $word_spacing word spacing adjustment
- * @param float $char_spacing
- * @param float $angle Text angle
- *
- * @return void
- */
- public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0)
+ public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0)
{
// Scale by the AA factor and DPI
$x = $this->_upscale($x);
$y = $this->_upscale($y);
$size = $this->_upscale($size) * self::FONT_SCALE;
- $h = $this->get_font_height_actual($font, $size);
+ $h = round($this->get_font_height_actual($font, $size));
$c = $this->_allocate_color($color);
// imagettftext() converts numeric entities to their respective
@@ -784,7 +658,7 @@ class GD implements Canvas
// eg:   will render rather than its character.
$text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $text);
- $text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8');
+ $text = mb_encode_numericentity($text, [0x0080, 0xff, 0, 0xff], 'UTF-8');
$font = $this->get_ttf_file($font);
@@ -797,61 +671,26 @@ class GD implements Canvas
// Not implemented
}
- /**
- * Add a named destination (similar to ... in html)
- *
- * @param string $anchorname The name of the named destination
- */
public function add_named_dest($anchorname)
{
// Not implemented
}
- /**
- * Add a link to the pdf
- *
- * @param string $url The url to link to
- * @param float $x The x position of the link
- * @param float $y The y position of the link
- * @param float $width The width of the link
- * @param float $height The height of the link
- */
public function add_link($url, $x, $y, $width, $height)
{
// Not implemented
}
- /**
- * Add meta information to the PDF
- *
- * @param string $label label of the value (Creator, Producer, etc.)
- * @param string $value the text to set
- */
- public function add_info($label, $value)
+ public function add_info(string $label, string $value): void
{
// N/A
}
- /**
- * @param string $view
- * @param array $options
- */
- public function set_default_view($view, $options = array())
+ public function set_default_view($view, $options = [])
{
// N/A
}
- /**
- * Calculates text size, in points
- *
- * @param string $text the text to be sized
- * @param string $font the desired font
- * @param float $size the desired font size
- * @param float $word_spacing word spacing, if any
- * @param float $char_spacing char spacing, if any
- *
- * @return float
- */
public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
{
$font = $this->get_ttf_file($font);
@@ -863,7 +702,7 @@ class GD implements Canvas
// eg:   will render rather than its character.
$text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $text);
- $text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8');
+ $text = mb_encode_numericentity($text, [0x0080, 0xffff, 0, 0xffff], 'UTF-8');
// FIXME: word spacing
list($x1, , $x2) = imagettfbbox($size, 0, $font, $text);
@@ -873,11 +712,15 @@ class GD implements Canvas
}
/**
- * @param $font
+ * @param string|null $font
* @return string
*/
public function get_ttf_file($font)
{
+ if ($font === null) {
+ $font = "";
+ }
+
if ( stripos($font, ".ttf") === false ) {
$font .= ".ttf";
}
@@ -901,13 +744,6 @@ class GD implements Canvas
return $font;
}
- /**
- * Calculates font height, in points
- *
- * @param string $font
- * @param float $size
- * @return float
- */
public function get_font_height($font, $size)
{
$size = $this->_upscale($size) * self::FONT_SCALE;
@@ -917,7 +753,13 @@ class GD implements Canvas
return $this->_downscale($height);
}
- private function get_font_height_actual($font, $size)
+ /**
+ * @param string $font
+ * @param float $size
+ *
+ * @return float
+ */
+ protected function get_font_height_actual($font, $size)
{
$font = $this->get_ttf_file($font);
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
@@ -927,22 +769,12 @@ class GD implements Canvas
return ($y2 - $y1) * $ratio;
}
- /**
- * @param string $font
- * @param float $size
- * @return float
- */
public function get_font_baseline($font, $size)
{
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
return $this->get_font_height($font, $size) / $ratio;
}
- /**
- * Starts a new page
- *
- * Subsequent drawing operations will appear on the new page.
- */
public function new_page()
{
$this->_page_number++;
@@ -973,12 +805,17 @@ class GD implements Canvas
// N/A
}
- public function page_text()
+ public function page_script($callback): void
{
// N/A
}
- public function page_line()
+ public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
+ {
+ // N/A
+ }
+
+ public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
{
// N/A
}
@@ -987,10 +824,10 @@ class GD implements Canvas
* Streams the image to the client.
*
* @param string $filename The filename to present to the client.
- * @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
+ * @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
* 'page' => Number of the page to output (defaults to the first); 'Attachment': 1 or 0 (default 1).
*/
- public function stream($filename, $options = array())
+ public function stream($filename, $options = [])
{
if (headers_sent()) {
die("Unable to stream image: headers already sent");
@@ -1016,7 +853,7 @@ class GD implements Canvas
header("Cache-Control: private");
header("Content-Type: $contentType");
- $filename = str_replace(array("\n", "'"), "", basename($filename, ".$type")) . $extension;
+ $filename = str_replace(["\n", "'"], "", basename($filename, ".$type")) . $extension;
$attachment = $options["Attachment"] ? "attachment" : "inline";
header(Helpers::buildContentDispositionHeader($attachment, $filename));
@@ -1031,7 +868,7 @@ class GD implements Canvas
* 'page' => Number of the page to output (defaults to the first).
* @return string
*/
- public function output($options = array())
+ public function output($options = [])
{
ob_start();
@@ -1046,7 +883,7 @@ class GD implements Canvas
* @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
* 'page' => Number of the page to output (defaults to the first).
*/
- private function _output($options = array())
+ protected function _output($options = [])
{
if (!isset($options["type"])) $options["type"] = "png";
if (!isset($options["page"])) $options["page"] = 1;
@@ -1060,8 +897,8 @@ class GD implements Canvas
// Perform any antialiasing
if ($this->_aa_factor != 1) {
- $dst_w = $this->_actual_width / $this->_aa_factor;
- $dst_h = $this->_actual_height / $this->_aa_factor;
+ $dst_w = round($this->_actual_width / $this->_aa_factor);
+ $dst_h = round($this->_actual_height / $this->_aa_factor);
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagecopyresampled($dst, $img, 0, 0, 0, 0,
$dst_w, $dst_h,
diff --git a/library/vendor/dompdf/src/Adapter/PDFLib.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/PDFLib.php
similarity index 72%
rename from library/vendor/dompdf/src/Adapter/PDFLib.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/PDFLib.php
index e999c580e..ee5ae8dd2 100644
--- a/library/vendor/dompdf/src/Adapter/PDFLib.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/PDFLib.php
@@ -1,20 +1,17 @@
- * @author Helmut Tischer
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf\Adapter;
use Dompdf\Canvas;
use Dompdf\Dompdf;
-use Dompdf\Helpers;
use Dompdf\Exception;
+use Dompdf\FontMetrics;
+use Dompdf\Helpers;
use Dompdf\Image\Cache;
-use Dompdf\PhpEvaluator;
/**
* PDF rendering interface
@@ -37,9 +34,9 @@ class PDFLib implements Canvas
/**
* Dimensions of paper sizes in points
*
- * @var array;
+ * @var array
*/
- static public $PAPER_SIZES = array(); // Set to Dompdf\Adapter\CPDF::$PAPER_SIZES below.
+ public static $PAPER_SIZES = []; // Set to Dompdf\Adapter\CPDF::$PAPER_SIZES below.
/**
* Whether to create PDFs in memory or on disk
@@ -53,7 +50,7 @@ class PDFLib implements Canvas
*
* @var null|int
*/
- static private $MAJOR_VERSION = null;
+ protected static $MAJOR_VERSION = null;
/**
@@ -61,7 +58,7 @@ class PDFLib implements Canvas
*
* @var array
*/
- static public $nativeFontsTpPDFLib = array(
+ public static $nativeFontsTpPDFLib = [
"courier" => "Courier",
"courier-bold" => "Courier-Bold",
"courier-oblique" => "Courier-Oblique",
@@ -78,151 +75,139 @@ class PDFLib implements Canvas
"symbol" => "Symbol",
"zapfdinbats" => "ZapfDingbats",
"zapfdingbats" => "ZapfDingbats",
- );
+ ];
/**
* @var \Dompdf\Dompdf
*/
- private $_dompdf;
+ protected $_dompdf;
/**
* Instance of PDFLib class
*
* @var \PDFLib
*/
- private $_pdf;
+ protected $_pdf;
/**
* Name of temporary file used for PDFs created on disk
*
* @var string
*/
- private $_file;
+ protected $_file;
/**
* PDF width, in points
*
* @var float
*/
- private $_width;
+ protected $_width;
/**
* PDF height, in points
*
* @var float
*/
- private $_height;
+ protected $_height;
/**
* Last fill color used
*
* @var array
*/
- private $_last_fill_color;
+ protected $_last_fill_color;
/**
* Last stroke color used
*
* @var array
*/
- private $_last_stroke_color;
+ protected $_last_stroke_color;
/**
* The current opacity level
*
- * @var array
+ * @var float|null
*/
- private $_current_opacity;
+ protected $_current_opacity;
/**
* Cache of image handles
*
* @var array
*/
- private $_imgs;
+ protected $_imgs;
/**
* Cache of font handles
*
* @var array
*/
- private $_fonts;
+ protected $_fonts;
/**
* Cache of fontFile checks
*
* @var array
*/
- private $_fontsFiles;
+ protected $_fontsFiles;
/**
* List of objects (templates) to add to multiple pages
*
* @var array
*/
- private $_objs;
+ protected $_objs;
/**
* List of gstate objects created for this PDF (for reuse)
*
* @var array
*/
- private $_gstates = array();
+ protected $_gstates = [];
/**
* Current page number
*
* @var int
*/
- private $_page_number;
+ protected $_page_number;
/**
* Total number of pages
*
* @var int
*/
- private $_page_count;
+ protected $_page_count;
/**
- * Text to display on every page
+ * Array of pages for accessing after rendering is initially complete
*
* @var array
*/
- private $_page_text;
+ protected $_pages;
- /**
- * Array of pages for accesing after rendering is initially complete
- *
- * @var array
- */
- private $_pages;
-
- /**
- * Class constructor
- *
- * @param string|array $paper The size of paper to use either a string (see {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}) or
- * an array(xmin,ymin,xmax,ymax)
- * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
- * @param Dompdf $dompdf
- */
- public function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf)
+ public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null)
{
if (is_array($paper)) {
- $size = $paper;
- } elseif (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) {
- $size = self::$PAPER_SIZES[mb_strtolower($paper)];
+ $size = array_map("floatval", $paper);
} else {
- $size = self::$PAPER_SIZES["letter"];
+ $paper = strtolower($paper);
+ $size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"];
}
- if (mb_strtolower($orientation) === "landscape") {
- list($size[2], $size[3]) = array($size[3], $size[2]);
+ if (strtolower($orientation) === "landscape") {
+ [$size[2], $size[3]] = [$size[3], $size[2]];
}
$this->_width = $size[2] - $size[0];
$this->_height = $size[3] - $size[1];
- $this->_dompdf = $dompdf;
+ if ($dompdf === null) {
+ $this->_dompdf = new Dompdf();
+ } else {
+ $this->_dompdf = $dompdf;
+ }
$this->_pdf = new \PDFLib();
@@ -231,7 +216,9 @@ class PDFLib implements Canvas
$this->setPDFLibParameter("license", $license);
}
- $this->setPDFLibParameter("textformat", "utf8");
+ if ($this->getPDFLibMajorVersion() < 10) {
+ $this->setPDFLibParameter("textformat", "utf8");
+ }
if ($this->getPDFLibMajorVersion() >= 7) {
$this->setPDFLibParameter("errorpolicy", "return");
// $this->_pdf->set_option('logging={filename=' . \APP_PATH . '/logs/pdflib.log classes={api=1 warning=2}}');
@@ -267,16 +254,12 @@ class PDFLib implements Canvas
$this->_pdf->begin_page_ext($this->_width, $this->_height, "");
$this->_page_number = $this->_page_count = 1;
- $this->_page_text = array();
- $this->_imgs = array();
- $this->_fonts = array();
- $this->_objs = array();
+ $this->_imgs = [];
+ $this->_fonts = [];
+ $this->_objs = [];
}
- /**
- * @return Dompdf
- */
function get_dompdf()
{
return $this->_dompdf;
@@ -310,13 +293,7 @@ class PDFLib implements Canvas
return $this->_pdf;
}
- /**
- * Add meta information to the PDF
- *
- * @param string $label label of the value (Creator, Producter, etc.)
- * @param string $value the text to set
- */
- public function add_info($label, $value)
+ public function add_info(string $label, string $value): void
{
$this->_pdf->set_info($label, $value);
}
@@ -338,9 +315,13 @@ class PDFLib implements Canvas
public function open_object()
{
$this->_pdf->suspend_page("");
- $ret = $this->_pdf->begin_template($this->_width, $this->_height);
+ if ($this->getPDFLibMajorVersion() >= 7) {
+ $ret = $this->_pdf->begin_template_ext($this->_width, $this->_height, null);
+ } else {
+ $ret = $this->_pdf->begin_template($this->_width, $this->_height);
+ }
$this->_pdf->save();
- $this->_objs[$ret] = array("start_page" => $this->_page_number);
+ $this->_objs[$ret] = ["start_page" => $this->_page_number];
return $ret;
}
@@ -367,7 +348,11 @@ class PDFLib implements Canvas
public function close_object()
{
$this->_pdf->restore();
- $this->_pdf->end_template();
+ if ($this->getPDFLibMajorVersion() >= 7) {
+ $this->_pdf->end_template_ext($this->_width, $this->_height);
+ } else {
+ $this->_pdf->end_template();
+ }
$this->_pdf->resume_page("pagenumber=" . $this->_page_number);
}
@@ -450,36 +435,23 @@ class PDFLib implements Canvas
$this->_pdf->fit_image($obj, 0, 0, "");
}
}
-
}
- /**
- * @return float|mixed
- */
public function get_width()
{
return $this->_width;
}
- /**
- * @return float|mixed
- */
public function get_height()
{
return $this->_height;
}
- /**
- * @return int
- */
public function get_page_number()
{
return $this->_page_number;
}
- /**
- * @return int
- */
public function get_page_count()
{
return $this->_page_count;
@@ -493,9 +465,6 @@ class PDFLib implements Canvas
$this->_page_number = (int)$num;
}
- /**
- * @param int $count
- */
public function set_page_count($count)
{
$this->_page_count = (int)$count;
@@ -505,15 +474,25 @@ class PDFLib implements Canvas
* Sets the line style
*
* @param float $width
- * @param $cap
+ * @param string $cap
* @param string $join
* @param array $dash
- *
- * @return void
*/
protected function _set_line_style($width, $cap, $join, $dash)
{
- if (count($dash) == 1) {
+ if (!is_array($dash)) {
+ $dash = [];
+ }
+
+ // Work around PDFLib limitation with 0 dash length:
+ // Value 0 for option 'dasharray' is too small (minimum 1.5e-05)
+ foreach ($dash as &$d) {
+ if ($d == 0) {
+ $d = 1.5e-5;
+ }
+ }
+
+ if (count($dash) === 1) {
$dash[] = $dash[0];
}
@@ -599,7 +578,11 @@ class PDFLib implements Canvas
*/
protected function _set_stroke_color($color)
{
+ // TODO: we should check the current PDF stroke color
+ // instead of the cached value
if ($this->_last_stroke_color == $color) {
+ // FIXME: do nothing, this optimization is broken by the
+ // stroke being set as a side effect of other operations
//return;
}
@@ -612,16 +595,16 @@ class PDFLib implements Canvas
if (isset($color[3])) {
$type = "cmyk";
- list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
+ list($c1, $c2, $c3, $c4) = [$color[0], $color[1], $color[2], $color[3]];
} elseif (isset($color[2])) {
$type = "rgb";
- list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
+ list($c1, $c2, $c3, $c4) = [$color[0], $color[1], $color[2], null];
} else {
$type = "gray";
- list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
+ list($c1, $c2, $c3, $c4) = [$color[0], $color[1], null, null];
}
- $this->_set_stroke_opacity($alpha);
+ $this->_set_stroke_opacity($alpha, "Normal");
$this->_pdf->setcolor("stroke", $type, $c1, $c2, $c3, $c4);
}
@@ -632,8 +615,12 @@ class PDFLib implements Canvas
*/
protected function _set_fill_color($color)
{
+ // TODO: we should check the current PDF fill color
+ // instead of the cached value
if ($this->_last_fill_color == $color) {
- return;
+ // FIXME: do nothing, this optimization is broken by the
+ // fill being set as a side effect of other operations
+ //return;
}
$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
@@ -645,28 +632,28 @@ class PDFLib implements Canvas
if (isset($color[3])) {
$type = "cmyk";
- list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
+ list($c1, $c2, $c3, $c4) = [$color[0], $color[1], $color[2], $color[3]];
} elseif (isset($color[2])) {
$type = "rgb";
- list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
+ list($c1, $c2, $c3, $c4) = [$color[0], $color[1], $color[2], null];
} else {
$type = "gray";
- list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
+ list($c1, $c2, $c3, $c4) = [$color[0], $color[1], null, null];
}
- $this->_set_fill_opacity($alpha);
+ $this->_set_fill_opacity($alpha, "Normal");
$this->_pdf->setcolor("fill", $type, $c1, $c2, $c3, $c4);
}
/**
* Sets the fill opacity
*
- * @param $opacity
- * @param $mode
+ * @param float $opacity
+ * @param string $mode
*/
public function _set_fill_opacity($opacity, $mode = "Normal")
{
- if ($mode === "Normal" && is_null($opacity) === false) {
+ if ($mode === "Normal" && isset($opacity)) {
$this->_set_gstate("opacityfill=$opacity");
}
}
@@ -674,25 +661,19 @@ class PDFLib implements Canvas
/**
* Sets the stroke opacity
*
- * @param $opacity
- * @param $mode
+ * @param float $opacity
+ * @param string $mode
*/
public function _set_stroke_opacity($opacity, $mode = "Normal")
{
- if ($mode === "Normal" && is_null($opacity) === false) {
+ if ($mode === "Normal" && isset($opacity)) {
$this->_set_gstate("opacitystroke=$opacity");
}
}
- /**
- * Sets the opacity
- *
- * @param $opacity
- * @param $mode
- */
- public function set_opacity($opacity, $mode = "Normal")
+ public function set_opacity(float $opacity, string $mode = "Normal"): void
{
- if ($mode === "Normal" && is_null($opacity) === false) {
+ if ($mode === "Normal") {
$this->_set_gstate("opacityfill=$opacity opacitystroke=$opacity");
$this->_current_opacity = $opacity;
}
@@ -714,7 +695,7 @@ class PDFLib implements Canvas
return $this->_pdf->set_gstate($gstate);
}
- public function set_default_view($view, $options = array())
+ public function set_default_view($view, $options = [])
{
// TODO
// http://www.pdflib.com/fileadmin/pdflib/pdf/manuals/PDFlib-8.0.2-API-reference.pdf
@@ -760,6 +741,8 @@ class PDFLib implements Canvas
$options .= " embedding=true";
}
+ $options .= " autosubsetting=" . ($this->_dompdf->getOptions()->getIsFontSubsettingEnabled() === false ? "false" : "true");
+
if (is_null($encoding)) {
// Unicode encoding is only available for the commerical
// version of PDFlib and not PDFlib-Lite
@@ -849,18 +832,9 @@ class PDFLib implements Canvas
return $this->_height - $y;
}
- /**
- * @param float $x1
- * @param float $y1
- * @param float $x2
- * @param float $y2
- * @param array $color
- * @param float $width
- * @param array $style
- */
- public function line($x1, $y1, $x2, $y2, $color, $width, $style = null)
+ public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
{
- $this->_set_line_style($width, "butt", "", $style);
+ $this->_set_line_style($width, $cap, "", $style);
$this->_set_stroke_color($color);
$y1 = $this->y($y1);
@@ -873,62 +847,23 @@ class PDFLib implements Canvas
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
}
- /**
- * Draw line at the specified coordinates on every page.
- *
- * See {@link Style::munge_color()} for the format of the colour array.
- *
- * @param float $x1
- * @param float $y1
- * @param float $x2
- * @param float $y2
- * @param array $color
- * @param float $width
- * @param array $style optional
- */
- public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = array())
+ public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
{
- $_t = 'line';
- $this->_page_text[] = compact('_t', 'x1', 'y1', 'x2', 'y2', 'color', 'width', 'style');
- }
-
- /**
- * @param float $x1
- * @param float $y1
- * @param float $r1
- * @param float $r2
- * @param float $astart
- * @param float $aend
- * @param array $color
- * @param float $width
- * @param array $style
- */
- public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array())
- {
- $this->_set_line_style($width, "butt", "", $style);
+ $this->_set_line_style($width, $cap, "", $style);
$this->_set_stroke_color($color);
- $y1 = $this->y($y1);
+ $y = $this->y($y);
- $this->_pdf->arc($x1, $y1, $r1, $astart, $aend);
+ $this->_pdf->arc($x, $y, $r1, $astart, $aend);
$this->_pdf->stroke();
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
}
- /**
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param array $color
- * @param float $width
- * @param null $style
- */
- public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null)
+ public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
{
$this->_set_stroke_color($color);
- $this->_set_line_style($width, "butt", "", $style);
+ $this->_set_line_style($width, $cap, "", $style);
$y1 = $this->y($y1) - $h;
@@ -938,13 +873,6 @@ class PDFLib implements Canvas
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
}
- /**
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param array $color
- */
public function filled_rectangle($x1, $y1, $w, $h, $color)
{
$this->_set_fill_color($color);
@@ -957,12 +885,6 @@ class PDFLib implements Canvas
$this->_set_fill_opacity($this->_current_opacity, "Normal");
}
- /**
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- */
public function clipping_rectangle($x1, $y1, $w, $h)
{
$this->_pdf->save();
@@ -973,32 +895,72 @@ class PDFLib implements Canvas
$this->_pdf->clip();
}
- /**
- * @param float $x1
- * @param float $y1
- * @param float $w
- * @param float $h
- * @param float $rTL
- * @param float $rTR
- * @param float $rBR
- * @param float $rBL
- */
public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
{
- $this->clipping_rectangle($x1, $y1, $w, $h);
+ if ($this->getPDFLibMajorVersion() < 9) {
+ //TODO: add PDFLib7 support
+ $this->clipping_rectangle($x1, $y1, $w, $h);
+ return;
+ }
+
+ $this->_pdf->save();
+
+ // we use 0,0 for the base coordinates for the path points
+ // since we're drawing the path at the $x1,$y1 coordinates
+
+ $path = 0;
+ //start: left edge, top end
+ $path = $this->_pdf->add_path_point($path, 0, 0 - $rTL + $h, "move", "");
+ // line: left edge, bottom end
+ $path = $this->_pdf->add_path_point($path, 0, 0 + $rBL, "line", "");
+ // curve: bottom-left corner
+ if ($rBL > 0) {
+ $path = $this->_pdf->add_path_point($path, 0 + $rBL, 0, "elliptical", "radius=$rBL clockwise=false");
+ }
+ // line: bottom edge, left end
+ $path = $this->_pdf->add_path_point($path, 0 - $rBR + $w, 0, "line", "");
+ // curve: bottom-right corner
+ if ($rBR > 0) {
+ $path = $this->_pdf->add_path_point($path, 0 + $w, 0 + $rBR, "elliptical", "radius=$rBR clockwise=false");
+ }
+ // line: right edge, top end
+ $path = $this->_pdf->add_path_point($path, 0 + $w, 0 - $rTR + $h, "line", "");
+ // curve: top-right corner
+ if ($rTR > 0) {
+ $path = $this->_pdf->add_path_point($path, 0 - $rTR + $w, 0 + $h, "elliptical", "radius=$rTR clockwise=false");
+ }
+ // line: top edge, left end
+ $path = $this->_pdf->add_path_point($path, 0 + $rTL, 0 + $h, "line", "");
+ // curve: top-left corner
+ if ($rTL > 0) {
+ $path = $this->_pdf->add_path_point($path, 0, 0 - $rTL + $h, "elliptical", "radius=$rTL clockwise=false");
+ }
+ $this->_pdf->draw_path($path, $x1, $this->_height-$y1-$h, "clip=true");
+ }
+
+ public function clipping_polygon(array $points): void
+ {
+ $this->_pdf->save();
+
+ $y = $this->y(array_pop($points));
+ $x = array_pop($points);
+ $this->_pdf->moveto($x, $y);
+
+ while (count($points) > 1) {
+ $y = $this->y(array_pop($points));
+ $x = array_pop($points);
+ $this->_pdf->lineto($x, $y);
+ }
+
+ $this->_pdf->closepath();
+ $this->_pdf->clip();
}
- /**
- *
- */
public function clipping_end()
{
$this->_pdf->restore();
}
- /**
- *
- */
public function save()
{
$this->_pdf->save();
@@ -1009,11 +971,6 @@ class PDFLib implements Canvas
$this->_pdf->restore();
}
- /**
- * @param $angle
- * @param $x
- * @param $y
- */
public function rotate($angle, $x, $y)
{
$pdf = $this->_pdf;
@@ -1022,12 +979,6 @@ class PDFLib implements Canvas
$pdf->translate(-$x, -$this->_height + $y);
}
- /**
- * @param $angle_x
- * @param $angle_y
- * @param $x
- * @param $y
- */
public function skew($angle_x, $angle_y, $x, $y)
{
$pdf = $this->_pdf;
@@ -1036,12 +987,6 @@ class PDFLib implements Canvas
$pdf->translate(-$x, -$this->_height + $y);
}
- /**
- * @param $s_x
- * @param $s_y
- * @param $x
- * @param $y
- */
public function scale($s_x, $s_y, $x, $y)
{
$pdf = $this->_pdf;
@@ -1050,36 +995,17 @@ class PDFLib implements Canvas
$pdf->translate(-$x, -$this->_height + $y);
}
- /**
- * @param $t_x
- * @param $t_y
- */
public function translate($t_x, $t_y)
{
$this->_pdf->translate($t_x, -$t_y);
}
- /**
- * @param $a
- * @param $b
- * @param $c
- * @param $d
- * @param $e
- * @param $f
- */
public function transform($a, $b, $c, $d, $e, $f)
{
$this->_pdf->concat($a, $b, $c, $d, $e, $f);
}
- /**
- * @param array $points
- * @param array $color
- * @param null $width
- * @param null $style
- * @param bool $fill
- */
- public function polygon($points, $color, $width = null, $style = null, $fill = false)
+ public function polygon($points, $color, $width = null, $style = [], $fill = false)
{
$this->_set_fill_color($color);
$this->_set_stroke_color($color);
@@ -1108,16 +1034,7 @@ class PDFLib implements Canvas
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
}
- /**
- * @param float $x
- * @param float $y
- * @param float $r
- * @param array $color
- * @param null $width
- * @param null $style
- * @param bool $fill
- */
- public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false)
+ public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
{
$this->_set_fill_color($color);
$this->_set_stroke_color($color);
@@ -1140,44 +1057,49 @@ class PDFLib implements Canvas
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
}
- /**
- * @param string $img_url
- * @param float $x
- * @param float $y
- * @param int $w
- * @param int $h
- * @param string $resolution
- */
- public function image($img_url, $x, $y, $w, $h, $resolution = "normal")
+ public function image($img, $x, $y, $w, $h, $resolution = "normal")
{
$w = (int)$w;
$h = (int)$h;
- $img_type = Cache::detect_type($img_url, $this->get_dompdf()->getHttpContext());
+ $img_type = Cache::detect_type($img, $this->get_dompdf()->getHttpContext());
- if (!isset($this->_imgs[$img_url])) {
- $this->_imgs[$img_url] = $this->_pdf->load_image($img_type, $img_url, "");
+ // Strip file:// prefix
+ if (substr($img, 0, 7) === "file://") {
+ $img = substr($img, 7);
}
- $img = $this->_imgs[$img_url];
+ if (!isset($this->_imgs[$img])) {
+ if (strtolower($img_type) === "svg") {
+ //FIXME: PDFLib loads SVG but returns error message "Function must not be called in 'page' scope"
+ $image_load_response = $this->_pdf->load_graphics($img_type, $img, "");
+ } else {
+ $image_load_response = $this->_pdf->load_image($img_type, $img, "");
+ }
+ if ($image_load_response === 0) {
+ //TODO: should do something with the error message
+ $error = $this->_pdf->get_errmsg();
+ return;
+ }
+ $this->_imgs[$img] = $image_load_response;
+ }
+
+ $img = $this->_imgs[$img];
$y = $this->y($y) - $h;
- $this->_pdf->fit_image($img, $x, $y, 'boxsize={' . "$w $h" . '} fitmethod=entire');
+ if (strtolower($img_type) === "svg") {
+ $this->_pdf->fit_graphics($img, $x, $y, 'boxsize={' . "$w $h" . '} fitmethod=entire');
+ } else {
+ $this->_pdf->fit_image($img, $x, $y, 'boxsize={' . "$w $h" . '} fitmethod=entire');
+ }
}
- /**
- * @param float $x
- * @param float $y
- * @param string $text
- * @param string $font
- * @param float $size
- * @param array $color
- * @param int $word_spacing
- * @param int $char_spacing
- * @param int $angle
- */
- public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_spacing = 0, $char_spacing = 0, $angle = 0)
+ public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0, $char_spacing = 0, $angle = 0)
{
+ if ($size == 0) {
+ return;
+ }
+
$fh = $this->_load_font($font);
$this->_pdf->setfont($fh, $size);
@@ -1194,9 +1116,6 @@ class PDFLib implements Canvas
$this->_set_fill_opacity($this->_current_opacity, "Normal");
}
- /**
- * @param string $code
- */
public function javascript($code)
{
if (strlen($this->_dompdf->getOptions()->getPdflibLicense()) > 0) {
@@ -1204,25 +1123,11 @@ class PDFLib implements Canvas
}
}
- /**
- * Add a named destination (similar to ... in html)
- *
- * @param string $anchorname The name of the named destination
- */
public function add_named_dest($anchorname)
{
$this->_pdf->add_nameddest($anchorname, "");
}
- /**
- * Add a link to the pdf
- *
- * @param string $url The url to link to
- * @param float $x The x position of the link
- * @param float $y The y position of the link
- * @param float $width The width of the link
- * @param float $height The height of the link
- */
public function add_link($url, $x, $y, $width, $height)
{
$y = $this->y($y) - $height;
@@ -1234,29 +1139,21 @@ class PDFLib implements Canvas
"contents={$url} destname=" . substr($url, 1) . " linewidth=0");
}
} else {
- list($proto, $host, $path, $file) = Helpers::explode_url($url);
-
- if ($proto == "" || $proto === "file://") {
- return; // Local links are not allowed
+ //TODO: PDFLib::create_action does not permit non-HTTP links for URI actions
+ $action = $this->_pdf->create_action("URI", "url={{$url}}");
+ // add the annotation only if the action was created
+ if ($action !== 0) {
+ $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={{$url}} action={activate=$action} linewidth=0");
}
- $url = Helpers::build_url($proto, $host, $path, $file);
- $url = '{' . rawurldecode($url) . '}';
-
- $action = $this->_pdf->create_action("URI", "url=" . $url);
- $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0");
}
}
- /**
- * @param string $text
- * @param string $font
- * @param float $size
- * @param int $word_spacing
- * @param int $letter_spacing
- * @return mixed
- */
- public function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0)
+ public function get_text_width($text, $font, $size, $word_spacing = 0.0, $letter_spacing = 0.0)
{
+ if ($size == 0) {
+ return 0.0;
+ }
+
$fh = $this->_load_font($font);
// Determine the additional width due to extra spacing
@@ -1265,37 +1162,31 @@ class PDFLib implements Canvas
if ($letter_spacing) {
$num_chars = mb_strlen($text);
- $delta += ($num_chars - $num_spaces) * $letter_spacing;
+ $delta += $num_chars * $letter_spacing;
}
return $this->_pdf->stringwidth($text, $fh, $size) + $delta;
}
- /**
- * @param string $font
- * @param float $size
- * @return float
- */
public function get_font_height($font, $size)
{
+ if ($size == 0) {
+ return 0.0;
+ }
+
$fh = $this->_load_font($font);
$this->_pdf->setfont($fh, $size);
- $asc = $this->_pdf->get_value("ascender", $fh);
- $desc = $this->_pdf->get_value("descender", $fh);
+ $asc = $this->_pdf->info_font($fh, "ascender", "fontsize=$size");
+ $desc = $this->_pdf->info_font($fh, "descender", "fontsize=$size");
// $desc is usually < 0,
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
- return $size * ($asc - $desc) * $ratio;
+ return (abs($asc) + abs($desc)) * $ratio;
}
- /**
- * @param string $font
- * @param float $size
- * @return float
- */
public function get_font_baseline($font, $size)
{
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
@@ -1304,51 +1195,55 @@ class PDFLib implements Canvas
}
/**
- * Writes text at the specified x and y coordinates on every page
+ * Processes a callback or script on every page.
*
- * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
- * with their current values.
+ * The callback function receives the four parameters `int $pageNumber`,
+ * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
+ * that order. If a script is passed as string, the variables `$PAGE_NUM`,
+ * `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing
+ * a script as string is deprecated and will be removed in a future version.
*
- * See {@link Style::munge_color()} for the format of the color array.
+ * This function can be used to add page numbers to all pages after the
+ * first one, for example.
*
- * @param float $x
- * @param float $y
- * @param string $text the text to write
- * @param string $font the font file to use
- * @param float $size the font size, in points
- * @param array $color
- * @param float $word_space word spacing adjustment
- * @param float $char_space char spacing adjustment
- * @param float $angle angle to write the text at, measured CW starting from the x-axis
+ * @param callable|string $callback The callback function or PHP script to process on every page
*/
- public function page_text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
+ public function page_script($callback): void
{
- $_t = "text";
- $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
+ if (is_string($callback)) {
+ $this->processPageScript(function (
+ int $PAGE_NUM,
+ int $PAGE_COUNT,
+ self $pdf,
+ FontMetrics $fontMetrics
+ ) use ($callback) {
+ eval($callback);
+ });
+ return;
+ }
+
+ $this->processPageScript($callback);
}
- //........................................................................
-
- /**
- * Processes a script on every page
- *
- * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
- *
- * This function can be used to add page numbers to all pages
- * after the first one, for example.
- *
- * @param string $code the script code
- * @param string $type the language type for script
- */
- public function page_script($code, $type = "text/php")
+ public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
{
- $_t = "script";
- $this->_page_text[] = compact("_t", "code", "type");
+ $this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) {
+ $text = str_replace(
+ ["{PAGE_NUM}", "{PAGE_COUNT}"],
+ [$pageNumber, $pageCount],
+ $text
+ );
+ $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
+ });
+ }
+
+ public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
+ {
+ $this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) {
+ $this->line($x1, $y1, $x2, $y2, $color, $width, $style);
+ });
}
- /**
- *
- */
public function new_page()
{
// Add objects to the current page
@@ -1359,44 +1254,15 @@ class PDFLib implements Canvas
$this->_page_number = ++$this->_page_count;
}
- /**
- * Add text to each page after rendering is complete
- */
- protected function _add_page_text()
+ protected function processPageScript(callable $callback): void
{
- if (!count($this->_page_text)) {
- return;
- }
-
- $eval = null;
$this->_pdf->suspend_page("");
for ($p = 1; $p <= $this->_page_count; $p++) {
$this->_pdf->resume_page("pagenumber=$p");
- foreach ($this->_page_text as $pt) {
- extract($pt);
-
- switch ($_t) {
- case "text":
- $text = str_replace(array("{PAGE_NUM}", "{PAGE_COUNT}"),
- array($p, $this->_page_count), $text);
- $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
- break;
-
- case "script":
- if (!$eval) {
- $eval = new PHPEvaluator($this);
- }
- $eval->evaluate($code, array('PAGE_NUM' => $p, 'PAGE_COUNT' => $this->_page_count));
- break;
-
- case 'line':
- $this->line( $x1, $y1, $x2, $y2, $color, $width, $style );
- break;
-
- }
- }
+ $fontMetrics = $this->_dompdf->getFontMetrics();
+ $callback($p, $this->_page_count, $this, $fontMetrics);
$this->_pdf->suspend_page("");
}
@@ -1405,13 +1271,9 @@ class PDFLib implements Canvas
}
/**
- * Streams the PDF to the client.
- *
- * @param string $filename The filename to present to the client.
- * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
* @throws Exception
*/
- public function stream($filename = "document.pdf", $options = array())
+ public function stream($filename = "document.pdf", $options = [])
{
if (headers_sent()) {
die("Unable to stream pdf: headers already sent");
@@ -1424,8 +1286,6 @@ class PDFLib implements Canvas
$options["Attachment"] = true;
}
- $this->_add_page_text();
-
if ($options["compress"]) {
$this->setPDFLibValue("compress", 6);
} else {
@@ -1447,7 +1307,7 @@ class PDFLib implements Canvas
header("Content-Type: application/pdf");
header("Content-Length: " . $size);
- $filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
+ $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf";
$attachment = $options["Attachment"] ? "attachment" : "inline";
header(Helpers::buildContentDispositionHeader($attachment, $filename));
@@ -1480,20 +1340,12 @@ class PDFLib implements Canvas
flush();
}
- /**
- * Returns the PDF as a string.
- *
- * @param array $options Associative array: 'compress' => 1 or 0 (default 1).
- * @return string
- */
- public function output($options = array())
+ public function output($options = [])
{
if (!isset($options["compress"])) {
$options["compress"] = true;
}
- $this->_add_page_text();
-
if ($options["compress"]) {
$this->setPDFLibValue("compress", 6);
} else {
@@ -1580,7 +1432,7 @@ class PDFLib implements Canvas
/**
* @return int
*/
- private function getPDFLibMajorVersion()
+ protected function getPDFLibMajorVersion()
{
if (is_null(self::$MAJOR_VERSION)) {
if (method_exists($this->_pdf, "get_option")) {
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Canvas.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Canvas.php
new file mode 100644
index 000000000..1812def58
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Canvas.php
@@ -0,0 +1,477 @@
+ alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ * @param float $width
+ * @param array $style
+ * @param string $cap `butt`, `round`, or `square`
+ */
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt");
+
+ /**
+ * Draws an arc
+ *
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
+ * $style and $cap parameters (aka dash and cap).
+ *
+ * @param float $x X coordinate of the arc
+ * @param float $y Y coordinate of the arc
+ * @param float $r1 Radius 1
+ * @param float $r2 Radius 2
+ * @param float $astart Start angle in degrees
+ * @param float $aend End angle in degrees
+ * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ * @param float $width
+ * @param array $style
+ * @param string $cap `butt`, `round`, or `square`
+ */
+ function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt");
+
+ /**
+ * Draws a rectangle at x1,y1 with width w and height h
+ *
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
+ * $style and $cap parameters (aka dash and cap).
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $w
+ * @param float $h
+ * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ * @param float $width
+ * @param array $style
+ * @param string $cap `butt`, `round`, or `square`
+ */
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt");
+
+ /**
+ * Draws a filled rectangle at x1,y1 with width w and height h
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $w
+ * @param float $h
+ * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ */
+ function filled_rectangle($x1, $y1, $w, $h, $color);
+
+ /**
+ * Starts a clipping rectangle at x1,y1 with width w and height h
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $w
+ * @param float $h
+ */
+ function clipping_rectangle($x1, $y1, $w, $h);
+
+ /**
+ * Starts a rounded clipping rectangle at x1,y1 with width w and height h
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $w
+ * @param float $h
+ * @param float $tl
+ * @param float $tr
+ * @param float $br
+ * @param float $bl
+ */
+ function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
+
+ /**
+ * Starts a clipping polygon
+ *
+ * @param float[] $points
+ */
+ public function clipping_polygon(array $points): void;
+
+ /**
+ * Ends the last clipping shape
+ */
+ function clipping_end();
+
+ /**
+ * Processes a callback on every page.
+ *
+ * The callback function receives the four parameters `int $pageNumber`,
+ * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
+ * that order.
+ *
+ * This function can be used to add page numbers to all pages after the
+ * first one, for example.
+ *
+ * @param callable $callback The callback function to process on every page
+ */
+ public function page_script($callback): void;
+
+ /**
+ * Writes text at the specified x and y coordinates on every page.
+ *
+ * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
+ * with their current values.
+ *
+ * @param float $x
+ * @param float $y
+ * @param string $text The text to write
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ * @param float $word_space Word spacing adjustment
+ * @param float $char_space Char spacing adjustment
+ * @param float $angle Angle to write the text at, measured clockwise starting from the x-axis
+ */
+ public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
+
+ /**
+ * Draws a line at the specified coordinates on every page.
+ *
+ * @param float $x1
+ * @param float $y1
+ * @param float $x2
+ * @param float $y2
+ * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ * @param float $width
+ * @param array $style
+ */
+ public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = []);
+
+ /**
+ * Save current state
+ */
+ function save();
+
+ /**
+ * Restore last state
+ */
+ function restore();
+
+ /**
+ * Rotate
+ *
+ * @param float $angle angle in degrees for counter-clockwise rotation
+ * @param float $x Origin abscissa
+ * @param float $y Origin ordinate
+ */
+ function rotate($angle, $x, $y);
+
+ /**
+ * Skew
+ *
+ * @param float $angle_x
+ * @param float $angle_y
+ * @param float $x Origin abscissa
+ * @param float $y Origin ordinate
+ */
+ function skew($angle_x, $angle_y, $x, $y);
+
+ /**
+ * Scale
+ *
+ * @param float $s_x scaling factor for width as percent
+ * @param float $s_y scaling factor for height as percent
+ * @param float $x Origin abscissa
+ * @param float $y Origin ordinate
+ */
+ function scale($s_x, $s_y, $x, $y);
+
+ /**
+ * Translate
+ *
+ * @param float $t_x movement to the right
+ * @param float $t_y movement to the bottom
+ */
+ function translate($t_x, $t_y);
+
+ /**
+ * Transform
+ *
+ * @param float $a
+ * @param float $b
+ * @param float $c
+ * @param float $d
+ * @param float $e
+ * @param float $f
+ */
+ function transform($a, $b, $c, $d, $e, $f);
+
+ /**
+ * Draws a polygon
+ *
+ * The polygon is formed by joining all the points stored in the $points
+ * array. $points has the following structure:
+ * ```
+ * array(0 => x1,
+ * 1 => y1,
+ * 2 => x2,
+ * 3 => y2,
+ * ...
+ * );
+ * ```
+ *
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
+ * $style parameter (aka dash).
+ *
+ * @param array $points
+ * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ * @param float $width
+ * @param array $style
+ * @param bool $fill Fills the polygon if true
+ */
+ function polygon($points, $color, $width = null, $style = [], $fill = false);
+
+ /**
+ * Draws a circle at $x,$y with radius $r
+ *
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
+ * $style parameter (aka dash).
+ *
+ * @param float $x
+ * @param float $y
+ * @param float $r
+ * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ * @param float $width
+ * @param array $style
+ * @param bool $fill Fills the circle if true
+ */
+ function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false);
+
+ /**
+ * Add an image to the pdf.
+ *
+ * The image is placed at the specified x and y coordinates with the
+ * given width and height.
+ *
+ * @param string $img The path to the image
+ * @param float $x X position
+ * @param float $y Y position
+ * @param float $w Width
+ * @param float $h Height
+ * @param string $resolution The resolution of the image
+ */
+ function image($img, $x, $y, $w, $h, $resolution = "normal");
+
+ /**
+ * Writes text at the specified x and y coordinates
+ *
+ * @param float $x
+ * @param float $y
+ * @param string $text The text to write
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
+ * where r, g, b, and alpha are float values between 0 and 1
+ * @param float $word_space Word spacing adjustment
+ * @param float $char_space Char spacing adjustment
+ * @param float $angle Angle to write the text at, measured clockwise starting from the x-axis
+ */
+ function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
+
+ /**
+ * Add a named destination (similar to ... in html)
+ *
+ * @param string $anchorname The name of the named destination
+ */
+ function add_named_dest($anchorname);
+
+ /**
+ * Add a link to the pdf
+ *
+ * @param string $url The url to link to
+ * @param float $x The x position of the link
+ * @param float $y The y position of the link
+ * @param float $width The width of the link
+ * @param float $height The height of the link
+ */
+ function add_link($url, $x, $y, $width, $height);
+
+ /**
+ * Add meta information to the PDF.
+ *
+ * @param string $label Label of the value (Creator, Producer, etc.)
+ * @param string $value The text to set
+ */
+ public function add_info(string $label, string $value): void;
+
+ /**
+ * Calculates text size, in points
+ *
+ * @param string $text The text to be sized
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ * @param float $word_spacing Word spacing, if any
+ * @param float $char_spacing Char spacing, if any
+ *
+ * @return float
+ */
+ function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0);
+
+ /**
+ * Calculates font height, in points
+ *
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ *
+ * @return float
+ */
+ function get_font_height($font, $size);
+
+ /**
+ * Returns the font x-height, in points
+ *
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ *
+ * @return float
+ */
+ //function get_font_x_height($font, $size);
+
+ /**
+ * Calculates font baseline, in points
+ *
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ *
+ * @return float
+ */
+ function get_font_baseline($font, $size);
+
+ /**
+ * Returns the PDF's width in points
+ *
+ * @return float
+ */
+ function get_width();
+
+ /**
+ * Returns the PDF's height in points
+ *
+ * @return float
+ */
+ function get_height();
+
+ /**
+ * Sets the opacity
+ *
+ * @param float $opacity
+ * @param string $mode
+ */
+ public function set_opacity(float $opacity, string $mode = "Normal"): void;
+
+ /**
+ * Sets the default view
+ *
+ * @param string $view
+ * 'XYZ' left, top, zoom
+ * 'Fit'
+ * 'FitH' top
+ * 'FitV' left
+ * 'FitR' left,bottom,right
+ * 'FitB'
+ * 'FitBH' top
+ * 'FitBV' left
+ * @param array $options
+ */
+ function set_default_view($view, $options = []);
+
+ /**
+ * @param string $code
+ */
+ function javascript($code);
+
+ /**
+ * Starts a new page
+ *
+ * Subsequent drawing operations will appear on the new page.
+ */
+ function new_page();
+
+ /**
+ * Streams the PDF to the client.
+ *
+ * @param string $filename The filename to present to the client.
+ * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
+ */
+ function stream($filename, $options = []);
+
+ /**
+ * Returns the PDF as a string.
+ *
+ * @param array $options Associative array: 'compress' => 1 or 0 (default 1).
+ *
+ * @return string
+ */
+ function output($options = []);
+}
diff --git a/library/vendor/dompdf/src/CanvasFactory.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/CanvasFactory.php
similarity index 93%
rename from library/vendor/dompdf/src/CanvasFactory.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/CanvasFactory.php
index b2bf1276a..86352e1dc 100644
--- a/library/vendor/dompdf/src/CanvasFactory.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/CanvasFactory.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf;
diff --git a/library/vendor/dompdf/src/Cellmap.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Cellmap.php
similarity index 61%
rename from library/vendor/dompdf/src/Cellmap.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Cellmap.php
index e426ef671..e6c1c68e6 100644
--- a/library/vendor/dompdf/src/Cellmap.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Cellmap.php
@@ -1,12 +1,12 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf;
+use Dompdf\FrameDecorator\AbstractFrameDecorator;
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
use Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator;
@@ -22,21 +22,18 @@ class Cellmap
{
/**
* Border style weight lookup for collapsed border resolution.
- *
- * @var array
*/
- protected static $_BORDER_STYLE_SCORE = array(
- "inset" => 1,
- "groove" => 2,
- "outset" => 3,
- "ridge" => 4,
- "dotted" => 5,
- "dashed" => 6,
- "solid" => 7,
+ protected const BORDER_STYLE_SCORE = [
"double" => 8,
- "hidden" => 9,
- "none" => 0,
- );
+ "solid" => 7,
+ "dashed" => 6,
+ "dotted" => 5,
+ "ridge" => 4,
+ "outset" => 3,
+ "groove" => 2,
+ "inset" => 1,
+ "none" => 0
+ ];
/**
* The table object this cellmap is attached to.
@@ -90,7 +87,7 @@ class Cellmap
/**
* 1D Array mapping frames to (multiple) pairs, keyed on frame_id.
*
- * @var Frame[]
+ * @var array[]
*/
protected $_frames;
@@ -109,14 +106,14 @@ class Cellmap
private $__row;
/**
- * Tells wether the columns' width can be modified
+ * Tells whether the columns' width can be modified
*
* @var bool
*/
private $_columns_locked = false;
/**
- * Tells wether the table has table-layout:fixed
+ * Tells whether the table has table-layout:fixed
*
* @var bool
*/
@@ -131,32 +128,26 @@ class Cellmap
$this->reset();
}
- /**
- *
- */
- public function reset()
+ public function reset(): void
{
$this->_num_rows = 0;
$this->_num_cols = 0;
- $this->_cells = array();
- $this->_frames = array();
+ $this->_cells = [];
+ $this->_frames = [];
if (!$this->_columns_locked) {
- $this->_columns = array();
+ $this->_columns = [];
}
- $this->_rows = array();
+ $this->_rows = [];
- $this->_borders = array();
+ $this->_borders = [];
$this->__col = $this->__row = 0;
}
- /**
- *
- */
- public function lock_columns()
+ public function lock_columns(): void
{
$this->_columns_locked = true;
}
@@ -170,9 +161,9 @@ class Cellmap
}
/**
- * @param $fixed
+ * @param bool $fixed
*/
- public function set_layout_fixed($fixed)
+ public function set_layout_fixed(bool $fixed)
{
$this->_fixed_layout = $fixed;
}
@@ -225,7 +216,7 @@ class Cellmap
public function &get_column($i)
{
if (!isset($this->_columns[$i])) {
- $this->_columns[$i] = array(
+ $this->_columns[$i] = [
"x" => 0,
"min-width" => 0,
"max-width" => 0,
@@ -233,7 +224,7 @@ class Cellmap
"absolute" => 0,
"percent" => 0,
"auto" => true,
- );
+ ];
}
return $this->_columns[$i];
@@ -255,11 +246,11 @@ class Cellmap
public function &get_row($j)
{
if (!isset($this->_rows[$j])) {
- $this->_rows[$j] = array(
+ $this->_rows[$j] = [
"y" => 0,
"first-column" => 0,
"height" => null,
- );
+ ];
}
return $this->_rows[$j];
@@ -276,11 +267,11 @@ class Cellmap
public function get_border($i, $j, $h_v, $prop = null)
{
if (!isset($this->_borders[$i][$j][$h_v])) {
- $this->_borders[$i][$j][$h_v] = array(
+ $this->_borders[$i][$j][$h_v] = [
"width" => 0,
"style" => "solid",
"color" => "black",
- );
+ ];
}
if (isset($prop)) {
@@ -298,18 +289,18 @@ class Cellmap
*/
public function get_border_properties($i, $j)
{
- return array(
+ return [
"top" => $this->get_border($i, $j, "horizontal"),
"right" => $this->get_border($i, $j + 1, "vertical"),
"bottom" => $this->get_border($i + 1, $j, "horizontal"),
"left" => $this->get_border($i, $j, "vertical"),
- );
+ ];
}
/**
* @param Frame $frame
*
- * @return null|Frame
+ * @return array|null
*/
public function get_spanned_cells(Frame $frame)
{
@@ -350,24 +341,26 @@ class Cellmap
throw new Exception("Frame not found in cellmap");
}
+ // Positions are stored relative to the table position
+ [$table_x, $table_y] = $this->_table->get_position();
$col = $this->_frames[$key]["columns"][0];
$row = $this->_frames[$key]["rows"][0];
if (!isset($this->_columns[$col])) {
$_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs.";
- $x = 0;
+ $x = $table_x;
} else {
- $x = $this->_columns[$col]["x"];
+ $x = $table_x + $this->_columns[$col]["x"];
}
if (!isset($this->_rows[$row])) {
$_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs.";
- $y = 0;
+ $y = $table_y;
} else {
- $y = $this->_rows[$row]["y"];
+ $y = $table_y + $this->_rows[$row]["y"];
}
- return array($x, $y, "x" => $x, "y" => $y);
+ return [$x, $y, "x" => $x, "y" => $y];
}
/**
@@ -434,89 +427,130 @@ class Cellmap
$col =& $this->get_column($j);
$col["used-width"] = $width;
$next_col =& $this->get_column($j + 1);
- $next_col["x"] = $next_col["x"] + $width;
+ $next_col["x"] = $col["x"] + $width;
}
/**
* @param int $i
- * @param mixed $height
+ * @param long $height
*/
public function set_row_height($i, $height)
{
$row =& $this->get_row($i);
+ if ($height > $row["height"]) {
+ $row["height"] = $height;
+ }
+ $next_row =& $this->get_row($i + 1);
+ $next_row["y"] = $row["y"] + $row["height"];
+ }
- if ($row["height"] !== null && $height <= $row["height"]) {
+ /**
+ * https://www.w3.org/TR/CSS21/tables.html#border-conflict-resolution
+ *
+ * @param int $i
+ * @param int $j
+ * @param string $h_v `horizontal` or `vertical`
+ * @param array $border_spec
+ */
+ protected function resolve_border(int $i, int $j, string $h_v, array $border_spec): void
+ {
+ if (!isset($this->_borders[$i][$j][$h_v])) {
+ $this->_borders[$i][$j][$h_v] = $border_spec;
return;
}
- $row["height"] = $height;
- $next_row =& $this->get_row($i + 1);
- $next_row["y"] = $row["y"] + $height;
+ $border = $this->_borders[$i][$j][$h_v];
- }
-
- /**
- * @param int $i
- * @param int $j
- * @param mixed $h_v
- * @param mixed $border_spec
- *
- * @return mixed
- */
- protected function _resolve_border($i, $j, $h_v, $border_spec)
- {
$n_width = $border_spec["width"];
$n_style = $border_spec["style"];
-
- if (!isset($this->_borders[$i][$j][$h_v])) {
- $this->_borders[$i][$j][$h_v] = $border_spec;
-
- return $this->_borders[$i][$j][$h_v]["width"];
- }
-
- $border = & $this->_borders[$i][$j][$h_v];
-
$o_width = $border["width"];
$o_style = $border["style"];
- if (($n_style === "hidden" ||
- $n_width > $o_width ||
- $o_style === "none")
-
- or
-
- ($o_width == $n_width &&
- in_array($n_style, self::$_BORDER_STYLE_SCORE) &&
- self::$_BORDER_STYLE_SCORE[$n_style] > self::$_BORDER_STYLE_SCORE[$o_style])
- ) {
- $border = $border_spec;
+ if ($o_style === "hidden") {
+ return;
}
- return $border["width"];
+ // A style of `none` has lowest priority independent of its specified
+ // width here, as its resolved width is always 0
+ if ($n_style === "hidden" || $n_width > $o_width
+ || ($o_width == $n_width
+ && isset(self::BORDER_STYLE_SCORE[$n_style])
+ && isset(self::BORDER_STYLE_SCORE[$o_style])
+ && self::BORDER_STYLE_SCORE[$n_style] > self::BORDER_STYLE_SCORE[$o_style])
+ ) {
+ $this->_borders[$i][$j][$h_v] = $border_spec;
+ }
}
/**
- * @param Frame $frame
+ * Get the resolved border properties for the given frame.
+ *
+ * @param AbstractFrameDecorator $frame
+ *
+ * @return array[]
*/
- public function add_frame(Frame $frame)
+ protected function get_resolved_border(AbstractFrameDecorator $frame): array
+ {
+ $key = $frame->get_id();
+ $columns = $this->_frames[$key]["columns"];
+ $rows = $this->_frames[$key]["rows"];
+
+ $first_col = $columns[0];
+ $last_col = $columns[count($columns) - 1];
+ $first_row = $rows[0];
+ $last_row = $rows[count($rows) - 1];
+
+ $max_top = null;
+ $max_bottom = null;
+ $max_left = null;
+ $max_right = null;
+
+ foreach ($columns as $col) {
+ $top = $this->_borders[$first_row][$col]["horizontal"];
+ $bottom = $this->_borders[$last_row + 1][$col]["horizontal"];
+
+ if ($max_top === null || $top["width"] > $max_top["width"]) {
+ $max_top = $top;
+ }
+ if ($max_bottom === null || $bottom["width"] > $max_bottom["width"]) {
+ $max_bottom = $bottom;
+ }
+ }
+
+ foreach ($rows as $row) {
+ $left = $this->_borders[$row][$first_col]["vertical"];
+ $right = $this->_borders[$row][$last_col + 1]["vertical"];
+
+ if ($max_left === null || $left["width"] > $max_left["width"]) {
+ $max_left = $left;
+ }
+ if ($max_right === null || $right["width"] > $max_right["width"]) {
+ $max_right = $right;
+ }
+ }
+
+ return [$max_top, $max_right, $max_bottom, $max_left];
+ }
+
+ /**
+ * @param AbstractFrameDecorator $frame
+ */
+ public function add_frame(Frame $frame): void
{
$style = $frame->get_style();
$display = $style->display;
- $collapse = $this->_table->get_style()->border_collapse == "collapse";
+ $collapse = $this->_table->get_style()->border_collapse === "collapse";
- // Recursively add the frames within tables, table-row-groups and table-rows
- if ($display === "table-row" ||
- $display === "table" ||
- $display === "inline-table" ||
- in_array($display, TableFrameDecorator::$ROW_GROUPS)
+ // Recursively add the frames within the table, its row groups and rows
+ if ($frame === $this->_table
+ || $display === "table-row"
+ || in_array($display, TableFrameDecorator::ROW_GROUPS, true)
) {
$start_row = $this->__row;
+
foreach ($frame->get_children() as $child) {
- // Ignore all Text frames and :before/:after pseudo-selector elements.
- if (!($child instanceof FrameDecorator\Text) && $child->get_node()->nodeName !== 'dompdf_generated') {
- $this->add_frame($child);
- }
+ $this->add_frame($child);
}
if ($display === "table-row") {
@@ -531,45 +565,55 @@ class Cellmap
$this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1));
$this->_frames[$key]["frame"] = $frame;
- if ($display !== "table-row" && $collapse) {
+ if ($collapse) {
$bp = $style->get_border_properties();
- // Resolve the borders
+ // Resolve vertical borders
for ($i = 0; $i < $num_rows + 1; $i++) {
- $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
- $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
+ $this->resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
+ $this->resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
}
+ // Resolve horizontal borders
for ($j = 0; $j < $this->_num_cols; $j++) {
- $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
- $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
+ $this->resolve_border($start_row, $j, "horizontal", $bp["top"]);
+ $this->resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
}
+
+ if ($frame === $this->_table) {
+ // Clear borders because the cells are now using them. The
+ // border width still needs to be set to half the resolved
+ // width so that the table is positioned properly
+ [$top, $right, $bottom, $left] = $this->get_resolved_border($frame);
+
+ $style->set_used("border_top_width", $top["width"] / 2);
+ $style->set_used("border_right_width", $right["width"] / 2);
+ $style->set_used("border_bottom_width", $bottom["width"] / 2);
+ $style->set_used("border_left_width", $left["width"] / 2);
+ $style->set_used("border_style", "none");
+ } else {
+ // Clear borders for rows and row groups
+ $style->set_used("border_width", 0);
+ $style->set_used("border_style", "none");
+ }
+ }
+
+ if ($frame === $this->_table) {
+ // Apply resolved borders to table cells and calculate column
+ // widths after all frames have been added
+ $this->calculate_column_widths();
}
return;
}
- $node = $frame->get_node();
-
- // Determine where this cell is going
- $colspan = $node->getAttribute("colspan");
- $rowspan = $node->getAttribute("rowspan");
-
- if (!$colspan) {
- $colspan = 1;
- $node->setAttribute("colspan", 1);
- }
-
- if (!$rowspan) {
- $rowspan = 1;
- $node->setAttribute("rowspan", 1);
- }
+ // Add the frame to the cellmap
$key = $frame->get_id();
-
+ $node = $frame->get_node();
$bp = $style->get_border_properties();
-
- // Add the frame to the cellmap
- $max_left = $max_right = 0;
+ // Determine where this cell is going
+ $colspan = max((int) $node->getAttribute("colspan"), 1);
+ $rowspan = max((int) $node->getAttribute("rowspan"), 1);
// Find the next available column (fix by Ciro Mondueri)
$ac = $this->__col;
@@ -591,13 +635,11 @@ class Cellmap
if ($collapse) {
// Resolve vertical borders
- $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
- $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
+ $this->resolve_border($row, $this->__col, "vertical", $bp["left"]);
+ $this->resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]);
}
}
- $max_top = $max_bottom = 0;
-
// Columns:
for ($j = 0; $j < $colspan; $j++) {
$col = $this->__col + $j;
@@ -605,42 +647,84 @@ class Cellmap
if ($collapse) {
// Resolve horizontal borders
- $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
- $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
+ $this->resolve_border($this->__row, $col, "horizontal", $bp["top"]);
+ $this->resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]);
}
}
$this->_frames[$key]["frame"] = $frame;
- // Handle seperated border model
- if (!$collapse) {
- list($h, $v) = $this->_table->get_style()->border_spacing;
+ $this->__col += $colspan;
+ if ($this->__col > $this->_num_cols) {
+ $this->_num_cols = $this->__col;
+ }
+ }
- // Border spacing is effectively a margin between cells
- $v = $style->length_in_pt($v);
- if (is_numeric($v)) {
- $v = $v / 2;
- }
- $h = $style->length_in_pt($h);
- if (is_numeric($h)) {
- $h = $h / 2;
- }
- $style->margin = "$v $h";
+ /**
+ * Apply resolved borders to table cells and calculate column widths.
+ */
+ protected function calculate_column_widths(): void
+ {
+ $table = $this->_table;
+ $table_style = $table->get_style();
+ $collapse = $table_style->border_collapse === "collapse";
- // The additional 1/2 width gets added to the table proper
+ if ($collapse) {
+ $v_spacing = 0;
+ $h_spacing = 0;
} else {
- // Drop the frame's actual border
- $style->border_left_width = $max_left / 2;
- $style->border_right_width = $max_right / 2;
- $style->border_top_width = $max_top / 2;
- $style->border_bottom_width = $max_bottom / 2;
- $style->margin = "none";
+ // The additional 1/2 width gets added to the table proper
+ [$h, $v] = $table_style->border_spacing;
+ $v_spacing = $v / 2;
+ $h_spacing = $h / 2;
}
- if (!$this->_columns_locked) {
+ foreach ($this->_frames as $frame_info) {
+ /** @var TableCellFrameDecorator */
+ $frame = $frame_info["frame"];
+ $style = $frame->get_style();
+ $display = $style->display;
+
+ if ($display !== "table-cell") {
+ continue;
+ }
+
+ if ($collapse) {
+ // Set the resolved border at half width
+ [$top, $right, $bottom, $left] = $this->get_resolved_border($frame);
+
+ $style->set_used("border_top_width", $top["width"] / 2);
+ $style->set_used("border_top_style", $top["style"]);
+ $style->set_used("border_top_color", $top["color"]);
+ $style->set_used("border_right_width", $right["width"] / 2);
+ $style->set_used("border_right_style", $right["style"]);
+ $style->set_used("border_right_color", $right["color"]);
+ $style->set_used("border_bottom_width", $bottom["width"] / 2);
+ $style->set_used("border_bottom_style", $bottom["style"]);
+ $style->set_used("border_bottom_color", $bottom["color"]);
+ $style->set_used("border_left_width", $left["width"] / 2);
+ $style->set_used("border_left_style", $left["style"]);
+ $style->set_used("border_left_color", $left["color"]);
+ $style->set_used("margin", 0);
+ } else {
+ // Border spacing is effectively a margin between cells
+ $style->set_used("margin_top", $v_spacing);
+ $style->set_used("margin_bottom", $v_spacing);
+ $style->set_used("margin_left", $h_spacing);
+ $style->set_used("margin_right", $h_spacing);
+ }
+
+ if ($this->_columns_locked) {
+ continue;
+ }
+
+ $node = $frame->get_node();
+ $colspan = max((int) $node->getAttribute("colspan"), 1);
+ $first_col = $frame_info["columns"][0];
+
// Resolve the frame's width
if ($this->_fixed_layout) {
- list($frame_min, $frame_max) = array(0, 10e-10);
+ list($frame_min, $frame_max) = [0, 10e-10];
} else {
list($frame_min, $frame_max) = $frame->get_min_max_width();
}
@@ -648,12 +732,12 @@ class Cellmap
$width = $style->width;
$val = null;
- if (Helpers::is_percent($width)) {
+ if (Helpers::is_percent($width) && $colspan === 1) {
$var = "percent";
- $val = (float)rtrim($width, "% ") / $colspan;
- } else if ($width !== "auto") {
+ $val = (float)rtrim($width, "% ");
+ } elseif ($width !== "auto" && $colspan === 1) {
$var = "absolute";
- $val = $style->length_in_pt($frame_min) / $colspan;
+ $val = $frame_min;
}
$min = 0;
@@ -661,7 +745,7 @@ class Cellmap
for ($cs = 0; $cs < $colspan; $cs++) {
// Resolve the frame's width(s) with other cells
- $col =& $this->get_column($this->__col + $cs);
+ $col =& $this->get_column($first_col + $cs);
// Note: $var is either 'percent' or 'absolute'. We compare the
// requested percentage or absolute values with the existing widths
@@ -675,12 +759,12 @@ class Cellmap
$max += $col["max-width"];
}
- if ($frame_min > $min) {
+ if ($frame_min > $min && $colspan === 1) {
// The frame needs more space. Expand each sub-column
// FIXME try to avoid putting this dummy value when table-layout:fixed
- $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min) / $colspan);
+ $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min));
for ($c = 0; $c < $colspan; $c++) {
- $col =& $this->get_column($this->__col + $c);
+ $col =& $this->get_column($first_col + $c);
$col["min-width"] += $inc;
}
}
@@ -689,22 +773,24 @@ class Cellmap
// FIXME try to avoid putting this dummy value when table-layout:fixed
$inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan);
for ($c = 0; $c < $colspan; $c++) {
- $col =& $this->get_column($this->__col + $c);
+ $col =& $this->get_column($first_col + $c);
$col["max-width"] += $inc;
}
}
}
- $this->__col += $colspan;
- if ($this->__col > $this->_num_cols) {
- $this->_num_cols = $this->__col;
+ // Adjust absolute columns so that the absolute (and max) width is the
+ // largest minimum width of all cells. This accounts for cells without
+ // absolute width within an absolute column
+ foreach ($this->_columns as &$col) {
+ if ($col["absolute"] > 0) {
+ $col["absolute"] = $col["min-width"];
+ $col["max-width"] = $col["min-width"];
+ }
}
}
- /**
- *
- */
- public function add_row()
+ protected function add_row(): void
{
$this->__row++;
$this->_num_rows++;
@@ -727,7 +813,7 @@ class Cellmap
{
$key = $row->get_id();
if (!isset($this->_frames[$key])) {
- return; // Presumably this row has alredy been removed
+ return; // Presumably this row has already been removed
}
$this->__row = $this->_num_rows--;
@@ -775,7 +861,7 @@ class Cellmap
{
$key = $group->get_id();
if (!isset($this->_frames[$key])) {
- return; // Presumably this row has alredy been removed
+ return; // Presumably this row has already been removed
}
$iter = $group->get_first_child();
@@ -797,16 +883,18 @@ class Cellmap
public function update_row_group(Frame $group, Frame $last_row)
{
$g_key = $group->get_id();
- $r_key = $last_row->get_id();
- $r_rows = $this->_frames[$g_key]["rows"];
- $this->_frames[$g_key]["rows"] = range($this->_frames[$g_key]["rows"][0], end($r_rows));
+ $first_index = $this->_frames[$g_key]["rows"][0];
+ $last_index = $first_index;
+ $row = $last_row;
+ while ($row = $row->get_prev_sibling()) {
+ $last_index++;
+ }
+
+ $this->_frames[$g_key]["rows"] = range($first_index, $last_index);
}
- /**
- *
- */
- public function assign_x_positions()
+ public function assign_x_positions(): void
{
// Pre-condition: widths must be resolved and assigned to columns and
// column[0]["x"] must be set.
@@ -822,17 +910,14 @@ class Cellmap
}
}
- /**
- *
- */
- public function assign_frame_heights()
+ public function assign_frame_heights(): void
{
// Pre-condition: widths and heights of each column & row must be
// calcluated
foreach ($this->_frames as $arr) {
$frame = $arr["frame"];
- $h = 0;
+ $h = 0.0;
foreach ($arr["rows"] as $row) {
if (!isset($this->_rows[$row])) {
// The row has been removed because of a page split, so skip it.
@@ -845,7 +930,7 @@ class Cellmap
if ($frame instanceof TableCellFrameDecorator) {
$frame->set_cell_height($h);
} else {
- $frame->get_style()->height = $h;
+ $frame->get_style()->set_used("height", $h);
}
}
}
@@ -853,13 +938,13 @@ class Cellmap
/**
* Re-adjust frame height if the table height is larger than its content
*/
- public function set_frame_heights($table_height, $content_height)
+ public function set_frame_heights(float $table_height, float $content_height): void
{
// Distribute the increased height proportionally amongst each row
foreach ($this->_frames as $arr) {
$frame = $arr["frame"];
- $h = 0;
+ $h = 0.0;
foreach ($arr["rows"] as $row) {
if (!isset($this->_rows[$row])) {
continue;
@@ -871,13 +956,13 @@ class Cellmap
if ($content_height > 0) {
$new_height = ($h / $content_height) * $table_height;
} else {
- $new_height = 0;
+ $new_height = 0.0;
}
if ($frame instanceof TableCellFrameDecorator) {
$frame->set_cell_height($new_height);
} else {
- $frame->get_style()->height = $new_height;
+ $frame->get_style()->set_used("height", $new_height);
}
}
}
@@ -887,7 +972,7 @@ class Cellmap
*
* @return string
*/
- public function __toString()
+ public function __toString(): string
{
$str = "";
$str .= "Columns: ";
@@ -896,19 +981,19 @@ class Cellmap
$str .= Helpers::pre_r($this->_rows, true);
$str .= "Frames: ";
- $arr = array();
+ $arr = [];
foreach ($this->_frames as $key => $val) {
- $arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]);
+ $arr[$key] = ["columns" => $val["columns"], "rows" => $val["rows"]];
}
$str .= Helpers::pre_r($arr, true);
if (php_sapi_name() == "cli") {
- $str = strip_tags(str_replace(array(" ", "", " "),
- array("\n", chr(27) . "[01;33m", chr(27) . "[0m"),
+ $str = strip_tags(str_replace([" ", "", " "],
+ ["\n", chr(27) . "[01;33m", chr(27) . "[0m"],
$str));
}
return $str;
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/src/Css/AttributeTranslator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/AttributeTranslator.php
similarity index 74%
rename from library/vendor/dompdf/src/Css/AttributeTranslator.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/AttributeTranslator.php
index c45d7413a..b2013e13c 100644
--- a/library/vendor/dompdf/src/Css/AttributeTranslator.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/AttributeTranslator.php
@@ -1,14 +1,13 @@
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\Css;
use Dompdf\Frame;
+use Dompdf\Helpers;
/**
* Translates HTML 4.0 attributes into CSS rules
@@ -22,33 +21,33 @@ class AttributeTranslator
// Munged data originally from
// http://www.w3.org/TR/REC-html40/index/attributes.html
// http://www.cs.tut.fi/~jkorpela/html2css.html
- static private $__ATTRIBUTE_LOOKUP = array(
+ private static $__ATTRIBUTE_LOOKUP = [
//'caption' => array ( 'align' => '', ),
- 'img' => array(
- 'align' => array(
+ 'img' => [
+ 'align' => [
'bottom' => 'vertical-align: baseline;',
'middle' => 'vertical-align: middle;',
'top' => 'vertical-align: top;',
'left' => 'float: left;',
'right' => 'float: right;'
- ),
+ ],
'border' => 'border: %0.2Fpx solid;',
- 'height' => 'height: %spx;',
+ 'height' => '_set_px_height',
'hspace' => 'padding-left: %1$0.2Fpx; padding-right: %1$0.2Fpx;',
'vspace' => 'padding-top: %1$0.2Fpx; padding-bottom: %1$0.2Fpx;',
- 'width' => 'width: %spx;',
- ),
- 'table' => array(
- 'align' => array(
+ 'width' => '_set_px_width',
+ ],
+ 'table' => [
+ 'align' => [
'left' => 'margin-left: 0; margin-right: auto;',
'center' => 'margin-left: auto; margin-right: auto;',
'right' => 'margin-left: auto; margin-right: 0;'
- ),
+ ],
'bgcolor' => 'background-color: %s;',
- 'border' => '!set_table_border',
- 'cellpadding' => '!set_table_cellpadding', //'border-spacing: %0.2F; border-collapse: separate;',
- 'cellspacing' => '!set_table_cellspacing',
- 'frame' => array(
+ 'border' => '_set_table_border',
+ 'cellpadding' => '_set_table_cellpadding', //'border-spacing: %0.2F; border-collapse: separate;',
+ 'cellspacing' => '_set_table_cellspacing',
+ 'frame' => [
'void' => 'border-style: none;',
'above' => 'border-top-style: solid;',
'below' => 'border-bottom-style: solid;',
@@ -58,44 +57,44 @@ class AttributeTranslator
'rhs' => 'border-right-style: solid;',
'box' => 'border-style: solid;',
'border' => 'border-style: solid;'
- ),
- 'rules' => '!set_table_rules',
+ ],
+ 'rules' => '_set_table_rules',
'width' => 'width: %s;',
- ),
- 'hr' => array(
- 'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly
+ ],
+ 'hr' => [
+ 'align' => '_set_hr_align', // Need to grab width to set 'left' & 'right' correctly
'noshade' => 'border-style: solid;',
- 'size' => '!set_hr_size', //'border-width: %0.2F px;',
+ 'size' => '_set_hr_size', //'border-width: %0.2F px;',
'width' => 'width: %s;',
- ),
- 'div' => array(
+ ],
+ 'div' => [
'align' => 'text-align: %s;',
- ),
- 'h1' => array(
+ ],
+ 'h1' => [
'align' => 'text-align: %s;',
- ),
- 'h2' => array(
+ ],
+ 'h2' => [
'align' => 'text-align: %s;',
- ),
- 'h3' => array(
+ ],
+ 'h3' => [
'align' => 'text-align: %s;',
- ),
- 'h4' => array(
+ ],
+ 'h4' => [
'align' => 'text-align: %s;',
- ),
- 'h5' => array(
+ ],
+ 'h5' => [
'align' => 'text-align: %s;',
- ),
- 'h6' => array(
+ ],
+ 'h6' => [
'align' => 'text-align: %s;',
- ),
+ ],
//TODO: translate more form element attributes
- 'input' => array(
- 'size' => '!set_input_width'
- ),
- 'p' => array(
+ 'input' => [
+ 'size' => '_set_input_width'
+ ],
+ 'p' => [
'align' => 'text-align: %s;',
- ),
+ ],
// 'col' => array(
// 'align' => '',
// 'valign' => '',
@@ -104,87 +103,87 @@ class AttributeTranslator
// 'align' => '',
// 'valign' => '',
// ),
- 'tbody' => array(
- 'align' => '!set_table_row_align',
- 'valign' => '!set_table_row_valign',
- ),
- 'td' => array(
+ 'tbody' => [
+ 'align' => '_set_table_row_align',
+ 'valign' => '_set_table_row_valign',
+ ],
+ 'td' => [
'align' => 'text-align: %s;',
- 'bgcolor' => '!set_background_color',
+ 'bgcolor' => '_set_background_color',
'height' => 'height: %s;',
'nowrap' => 'white-space: nowrap;',
'valign' => 'vertical-align: %s;',
'width' => 'width: %s;',
- ),
- 'tfoot' => array(
- 'align' => '!set_table_row_align',
- 'valign' => '!set_table_row_valign',
- ),
- 'th' => array(
+ ],
+ 'tfoot' => [
+ 'align' => '_set_table_row_align',
+ 'valign' => '_set_table_row_valign',
+ ],
+ 'th' => [
'align' => 'text-align: %s;',
- 'bgcolor' => '!set_background_color',
+ 'bgcolor' => '_set_background_color',
'height' => 'height: %s;',
'nowrap' => 'white-space: nowrap;',
'valign' => 'vertical-align: %s;',
'width' => 'width: %s;',
- ),
- 'thead' => array(
- 'align' => '!set_table_row_align',
- 'valign' => '!set_table_row_valign',
- ),
- 'tr' => array(
- 'align' => '!set_table_row_align',
- 'bgcolor' => '!set_table_row_bgcolor',
- 'valign' => '!set_table_row_valign',
- ),
- 'body' => array(
+ ],
+ 'thead' => [
+ 'align' => '_set_table_row_align',
+ 'valign' => '_set_table_row_valign',
+ ],
+ 'tr' => [
+ 'align' => '_set_table_row_align',
+ 'bgcolor' => '_set_table_row_bgcolor',
+ 'valign' => '_set_table_row_valign',
+ ],
+ 'body' => [
'background' => 'background-image: url(%s);',
- 'bgcolor' => '!set_background_color',
- 'link' => '!set_body_link',
- 'text' => '!set_color',
- ),
- 'br' => array(
+ 'bgcolor' => '_set_background_color',
+ 'link' => '_set_body_link',
+ 'text' => '_set_color',
+ ],
+ 'br' => [
'clear' => 'clear: %s;',
- ),
- 'basefont' => array(
- 'color' => '!set_color',
+ ],
+ 'basefont' => [
+ 'color' => '_set_color',
'face' => 'font-family: %s;',
- 'size' => '!set_basefont_size',
- ),
- 'font' => array(
- 'color' => '!set_color',
+ 'size' => '_set_basefont_size',
+ ],
+ 'font' => [
+ 'color' => '_set_color',
'face' => 'font-family: %s;',
- 'size' => '!set_font_size',
- ),
- 'dir' => array(
+ 'size' => '_set_font_size',
+ ],
+ 'dir' => [
'compact' => 'margin: 0.5em 0;',
- ),
- 'dl' => array(
+ ],
+ 'dl' => [
'compact' => 'margin: 0.5em 0;',
- ),
- 'menu' => array(
+ ],
+ 'menu' => [
'compact' => 'margin: 0.5em 0;',
- ),
- 'ol' => array(
+ ],
+ 'ol' => [
'compact' => 'margin: 0.5em 0;',
'start' => 'counter-reset: -dompdf-default-counter %d;',
'type' => 'list-style-type: %s;',
- ),
- 'ul' => array(
+ ],
+ 'ul' => [
'compact' => 'margin: 0.5em 0;',
'type' => 'list-style-type: %s;',
- ),
- 'li' => array(
+ ],
+ 'li' => [
'type' => 'list-style-type: %s;',
'value' => 'counter-reset: -dompdf-default-counter %d;',
- ),
- 'pre' => array(
+ ],
+ 'pre' => [
'width' => 'width: %s;',
- ),
- );
+ ],
+ ];
- static protected $_last_basefont_size = 3;
- static protected $_font_size_lookup = array(
+ protected static $_last_basefont_size = 3;
+ protected static $_font_size_lookup = [
// For basefont support
-3 => "4pt",
-2 => "5pt",
@@ -204,7 +203,7 @@ class AttributeTranslator
9 => "44pt",
10 => "52pt",
11 => "60pt",
- );
+ ];
/**
* @param Frame $frame
@@ -249,7 +248,6 @@ class AttributeTranslator
$style = ltrim($style);
$node->setAttribute(self::$_style_attr, $style);
}
-
}
/**
@@ -259,13 +257,10 @@ class AttributeTranslator
*
* @return string
*/
- static protected function _resolve_target(\DOMNode $node, $target, $value)
+ protected static function _resolve_target(\DOMNode $node, $target, $value)
{
- if ($target[0] === "!") {
- // Function call
- $func = "_" . mb_substr($target, 1);
-
- return self::$func($node, $value);
+ if ($target[0] === "_") {
+ return self::$target($node, $value);
}
return $value ? sprintf($target, $value) : "";
@@ -288,7 +283,7 @@ class AttributeTranslator
*
* @return \DOMNodeList|\DOMElement[]
*/
- static protected function get_cell_list(\DOMNode $node)
+ protected static function get_cell_list(\DOMNode $node)
{
$xpath = new \DOMXpath($node->ownerDocument);
@@ -317,7 +312,7 @@ class AttributeTranslator
*
* @return string
*/
- static protected function _get_valid_color($value)
+ protected static function _get_valid_color($value)
{
if (preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches)) {
$value = "#$matches[1]";
@@ -332,7 +327,7 @@ class AttributeTranslator
*
* @return string
*/
- static protected function _set_color(\DOMElement $node, $value)
+ protected static function _set_color(\DOMElement $node, $value)
{
$value = self::_get_valid_color($value);
@@ -345,20 +340,50 @@ class AttributeTranslator
*
* @return string
*/
- static protected function _set_background_color(\DOMElement $node, $value)
+ protected static function _set_background_color(\DOMElement $node, $value)
{
$value = self::_get_valid_color($value);
return "background-color: $value;";
}
+ protected static function _set_px_width(\DOMElement $node, string $value): string
+ {
+ $v = trim($value);
+
+ if (Helpers::is_percent($v)) {
+ return sprintf("width: %s;", $v);
+ }
+
+ if (is_numeric(mb_substr($v, 0, 1))) {
+ return sprintf("width: %spx;", (float) $v);
+ }
+
+ return "";
+ }
+
+ protected static function _set_px_height(\DOMElement $node, string $value): string
+ {
+ $v = trim($value);
+
+ if (Helpers::is_percent($v)) {
+ return sprintf("height: %s;", $v);
+ }
+
+ if (is_numeric(mb_substr($v, 0, 1))) {
+ return sprintf("height: %spx;", (float) $v);
+ }
+
+ return "";
+ }
+
/**
* @param \DOMElement $node
* @param string $value
*
* @return null
*/
- static protected function _set_table_cellpadding(\DOMElement $node, $value)
+ protected static function _set_table_cellpadding(\DOMElement $node, $value)
{
$cell_list = self::get_cell_list($node);
@@ -375,21 +400,9 @@ class AttributeTranslator
*
* @return string
*/
- static protected function _set_table_border(\DOMElement $node, $value)
+ protected static function _set_table_border(\DOMElement $node, $value)
{
- $cell_list = self::get_cell_list($node);
-
- foreach ($cell_list as $cell) {
- $style = rtrim($cell->getAttribute(self::$_style_attr));
- $style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;";
- $style = ltrim($style, ";");
- $cell->setAttribute(self::$_style_attr, $style);
- }
-
- $style = rtrim($node->getAttribute(self::$_style_attr), ";");
- $style .= "; border-width: $value" . "px; ";
-
- return ltrim($style, "; ");
+ return "border-width: $value" . "px;";
}
/**
@@ -398,7 +411,7 @@ class AttributeTranslator
*
* @return string
*/
- static protected function _set_table_cellspacing(\DOMElement $node, $value)
+ protected static function _set_table_cellspacing(\DOMElement $node, $value)
{
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
@@ -417,7 +430,7 @@ class AttributeTranslator
*
* @return null|string
*/
- static protected function _set_table_rules(\DOMElement $node, $value)
+ protected static function _set_table_rules(\DOMElement $node, $value)
{
$new_style = "; border-collapse: collapse;";
@@ -467,7 +480,7 @@ class AttributeTranslator
*
* @return string
*/
- static protected function _set_hr_size(\DOMElement $node, $value)
+ protected static function _set_hr_size(\DOMElement $node, $value)
{
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= "; border-width: " . max(0, $value - 2) . "; ";
@@ -481,7 +494,7 @@ class AttributeTranslator
*
* @return null|string
*/
- static protected function _set_hr_align(\DOMElement $node, $value)
+ protected static function _set_hr_align(\DOMElement $node, $value)
{
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$width = $node->getAttribute("width");
@@ -518,11 +531,11 @@ class AttributeTranslator
*
* @return null|string
*/
- static protected function _set_input_width(\DOMElement $node, $value)
+ protected static function _set_input_width(\DOMElement $node, $value)
{
if (empty($value)) { return null; }
- if ($node->hasAttribute("type") && in_array(strtolower($node->getAttribute("type")), array("text","password"))) {
+ if ($node->hasAttribute("type") && in_array(strtolower($node->getAttribute("type")), ["text","password"])) {
return sprintf("width: %Fem", (((int)$value * .65)+2));
} else {
return sprintf("width: %upx;", (int)$value);
@@ -535,7 +548,7 @@ class AttributeTranslator
*
* @return null
*/
- static protected function _set_table_row_align(\DOMElement $node, $value)
+ protected static function _set_table_row_align(\DOMElement $node, $value)
{
$cell_list = self::get_cell_list($node);
@@ -552,7 +565,7 @@ class AttributeTranslator
*
* @return null
*/
- static protected function _set_table_row_valign(\DOMElement $node, $value)
+ protected static function _set_table_row_valign(\DOMElement $node, $value)
{
$cell_list = self::get_cell_list($node);
@@ -569,7 +582,7 @@ class AttributeTranslator
*
* @return null
*/
- static protected function _set_table_row_bgcolor(\DOMElement $node, $value)
+ protected static function _set_table_row_bgcolor(\DOMElement $node, $value)
{
$cell_list = self::get_cell_list($node);
$value = self::_get_valid_color($value);
@@ -587,7 +600,7 @@ class AttributeTranslator
*
* @return null
*/
- static protected function _set_body_link(\DOMElement $node, $value)
+ protected static function _set_body_link(\DOMElement $node, $value)
{
$a_list = $node->getElementsByTagName("a");
$value = self::_get_valid_color($value);
@@ -605,7 +618,7 @@ class AttributeTranslator
*
* @return null
*/
- static protected function _set_basefont_size(\DOMElement $node, $value)
+ protected static function _set_basefont_size(\DOMElement $node, $value)
{
// FIXME: ? we don't actually set the font size of anything here, just
// the base size for later modification by tags.
@@ -620,7 +633,7 @@ class AttributeTranslator
*
* @return string
*/
- static protected function _set_font_size(\DOMElement $node, $value)
+ protected static function _set_font_size(\DOMElement $node, $value)
{
$style = $node->getAttribute(self::$_style_attr);
diff --git a/library/vendor/dompdf/src/Css/Color.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Color.php
similarity index 69%
rename from library/vendor/dompdf/src/Css/Color.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Color.php
index 737666300..28a9f562c 100644
--- a/library/vendor/dompdf/src/Css/Color.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Color.php
@@ -2,19 +2,16 @@
/**
* @package dompdf
- * @link http://dompdf.github.com/
- * @author Benj Carson
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf\Css;
use Dompdf\Helpers;
class Color
{
- static $cssColorNames = array(
+ static $cssColorNames = [
"aliceblue" => "F0F8FF",
"antiquewhite" => "FAEBD7",
"aqua" => "00FFFF",
@@ -162,21 +159,25 @@ class Color
"whitesmoke" => "F5F5F5",
"yellow" => "FFFF00",
"yellowgreen" => "9ACD32",
- );
+ ];
/**
- * @param $color
- * @return array|mixed|null|string
+ * @param array|string|null $color
+ * @return array|string|null
*/
static function parse($color)
{
+ if ($color === null) {
+ return null;
+ }
+
if (is_array($color)) {
// Assume the array has the right format...
// FIXME: should/could verify this.
return $color;
}
- static $cache = array();
+ static $cache = [];
$color = strtolower($color);
@@ -184,7 +185,7 @@ class Color
return $cache[$color];
}
- if (in_array($color, array("transparent", "inherit"))) {
+ if ($color === "transparent") {
return $cache[$color] = $color;
}
@@ -192,24 +193,43 @@ class Color
return $cache[$color] = self::getArray(self::$cssColorNames[$color]);
}
- $length = mb_strlen($color);
+ // https://www.w3.org/TR/css-color-4/#hex-notation
+ if (mb_substr($color, 0, 1) === "#") {
+ $length = mb_strlen($color);
+ $alpha = 1.0;
- // #rgb format
- if ($length == 4 && $color[0] === "#") {
- return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3]);
- } // #rgba format
- else if ($length == 5 && $color[0] === "#") {
- $alpha = round(hexdec($color[4] . $color[4])/255, 2);
- return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3], $alpha);
- } // #rrggbb format
- else if ($length == 7 && $color[0] === "#") {
- return $cache[$color] = self::getArray(mb_substr($color, 1, 6));
- } // #rrggbbaa format
- else if ($length == 9 && $color[0] === "#") {
- $alpha = round(hexdec(mb_substr($color, 7, 2))/255, 2);
- return $cache[$color] = self::getArray(mb_substr($color, 1, 8), $alpha);
- } // rgb( r,g,b ) / rgba( r,g,b,α ) format
- else if (mb_strpos($color, "rgb") !== false) {
+ // #rgb format
+ if ($length === 4) {
+ return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3]);
+ }
+
+ // #rgba format
+ if ($length === 5) {
+ if (ctype_xdigit($color[4])) {
+ $alpha = round(hexdec($color[4] . $color[4])/255, 2);
+ }
+ return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3], $alpha);
+ }
+
+ // #rrggbb format
+ if ($length === 7) {
+ return $cache[$color] = self::getArray(mb_substr($color, 1, 6));
+ }
+
+ // #rrggbbaa format
+ if ($length === 9) {
+ if (ctype_xdigit(mb_substr($color, 7, 2))) {
+ $alpha = round(hexdec(mb_substr($color, 7, 2))/255, 2);
+ }
+ return $cache[$color] = self::getArray(mb_substr($color, 1, 6), $alpha);
+ }
+
+ return null;
+ }
+
+ // rgb( r g b [/α] ) / rgb( r,g,b[,α] ) format and alias rgba()
+ // https://www.w3.org/TR/css-color-4/#rgb-functions
+ if (mb_substr($color, 0, 4) === "rgb(" || mb_substr($color, 0, 5) === "rgba(") {
$i = mb_strpos($color, "(");
$j = mb_strpos($color, ")");
@@ -218,38 +238,45 @@ class Color
return null;
}
- $triplet = explode(",", mb_substr($color, $i + 1, $j - $i - 1));
+ $value_decl = trim(mb_substr($color, $i + 1, $j - $i - 1));
- // alpha transparency
- // FIXME: not currently using transparency
- $alpha = 1.0;
- if (count($triplet) == 4) {
- $alpha = (float)(trim(array_pop($triplet)));
- // bad value, set to fully opaque
- if ($alpha > 1.0 || $alpha < 0.0) {
- $alpha = 1.0;
- }
+ if (mb_strpos($value_decl, ",") === false) {
+ // Space-separated values syntax `r g b` or `r g b / α`
+ $parts = preg_split("/\s*\/\s*/", $value_decl);
+ $triplet = preg_split("/\s+/", $parts[0]);
+ $alpha = $parts[1] ?? 1.0;
+ } else {
+ // Comma-separated values syntax `r, g, b` or `r, g, b, α`
+ $parts = preg_split("/\s*,\s*/", $value_decl);
+ $triplet = array_slice($parts, 0, 3);
+ $alpha = $parts[3] ?? 1.0;
}
- if (count($triplet) != 3) {
+ if (count($triplet) !== 3) {
return null;
}
- foreach (array_keys($triplet) as $c) {
- $triplet[$c] = trim($triplet[$c]);
+ // Parse alpha value
+ if (Helpers::is_percent($alpha)) {
+ $alpha = (float) $alpha / 100;
+ } else {
+ $alpha = (float) $alpha;
+ }
- if (Helpers::is_percent($triplet[$c])) {
- $triplet[$c] = round((float)$triplet[$c] * 2.55);
+ $alpha = max(0.0, min($alpha, 1.0));
+
+ foreach ($triplet as &$c) {
+ if (Helpers::is_percent($c)) {
+ $c = round((float) $c * 2.55);
}
}
return $cache[$color] = self::getArray(vsprintf("%02X%02X%02X", $triplet), $alpha);
-
}
// cmyk( c,m,y,k ) format
// http://www.w3.org/TR/css3-gcpm/#cmyk-colors
- else if (mb_strpos($color, "cmyk") !== false) {
+ if (mb_substr($color, 0, 5) === "cmyk(") {
$i = mb_strpos($color, "(");
$j = mb_strpos($color, ")");
@@ -264,24 +291,25 @@ class Color
return null;
}
- $values = array_map(function($c) {
+ $values = array_map(function ($c) {
return min(1.0, max(0.0, floatval(trim($c))));
}, $values);
return $cache[$color] = self::getArray($values);
}
+ // Invalid or unsupported color format
return null;
}
/**
- * @param $color
+ * @param array|string $color
* @param float $alpha
* @return array
*/
static function getArray($color, $alpha = 1.0)
{
- $c = array(null, null, null, null, "alpha" => $alpha, "hex" => null);
+ $c = [null, null, null, null, "alpha" => $alpha, "hex" => null];
if (is_array($color)) {
$c = $color;
@@ -292,6 +320,10 @@ class Color
$c["alpha"] = $alpha;
$c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])";
} else {
+ if (ctype_xdigit($color) === false || mb_strlen($color) !== 6) {
+ // invalid color value ... expected 6-character hex
+ return $c;
+ }
$c[0] = hexdec(mb_substr($color, 0, 2)) / 0xff;
$c[1] = hexdec(mb_substr($color, 2, 2)) / 0xff;
$c[2] = hexdec(mb_substr($color, 4, 2)) / 0xff;
@@ -299,7 +331,7 @@ class Color
$c["g"] = $c[1];
$c["b"] = $c[2];
$c["alpha"] = $alpha;
- $c["hex"] = sprintf("#%s%02X", mb_substr($color, 0, 6), round($alpha * 255));
+ $c["hex"] = sprintf("#%s%02X", $color, round($alpha * 255));
}
return $c;
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Style.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Style.php
new file mode 100644
index 000000000..2d4c9d798
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Style.php
@@ -0,0 +1,3743 @@
+margin_top = 10.0;
+ * echo $style->margin_top; // Returns `10.0`
+ * ```
+ *
+ * To declare a property from a string, use {@link Style::set_prop()}:
+ *
+ * ```
+ * $style->set_prop("margin_top", "1em");
+ * echo $style->get_specified("margin_top"); // Returns `1em`
+ * echo $style->margin_top; // Returns `12.0`, assuming the default font size
+ * ```
+ *
+ * Actual CSS parsing is performed in the {@link Stylesheet} class.
+ *
+ * @property string $azimuth
+ * @property string $background_attachment
+ * @property array|string $background_color
+ * @property string $background_image Image URL or `none`
+ * @property string $background_image_resolution
+ * @property array $background_position
+ * @property string $background_repeat
+ * @property array|string $background_size `cover`, `contain`, or `[width, height]`, each being a length, percentage, or `auto`
+ * @property string $border_collapse
+ * @property string $border_color Only use for setting all sides to the same color
+ * @property float[] $border_spacing Pair of `[horizontal, vertical]` spacing
+ * @property string $border_style Only use for setting all sides to the same style
+ * @property array|string $border_top_color
+ * @property array|string $border_right_color
+ * @property array|string $border_bottom_color
+ * @property array|string $border_left_color
+ * @property string $border_top_style Valid border style
+ * @property string $border_right_style Valid border style
+ * @property string $border_bottom_style Valid border style
+ * @property string $border_left_style Valid border style
+ * @property float $border_top_width Length in pt
+ * @property float $border_right_width Length in pt
+ * @property float $border_bottom_width Length in pt
+ * @property float $border_left_width Length in pt
+ * @property string $border_width Only use for setting all sides to the same width
+ * @property float|string $border_bottom_left_radius Radius in pt or a percentage value
+ * @property float|string $border_bottom_right_radius Radius in pt or a percentage value
+ * @property float|string $border_top_left_radius Radius in pt or a percentage value
+ * @property float|string $border_top_right_radius Radius in pt or a percentage value
+ * @property string $border_radius Only use for setting all corners to the same radius
+ * @property float|string $bottom Length in pt, a percentage value, or `auto`
+ * @property string $caption_side
+ * @property string $clear
+ * @property string $clip
+ * @property array|string $color
+ * @property string[]|string $content List of content components, `normal`, or `none`
+ * @property array|string $counter_increment Array defining the counters to increment or `none`
+ * @property array|string $counter_reset Array defining the counters to reset or `none`
+ * @property string $cue_after
+ * @property string $cue_before
+ * @property string $cue
+ * @property string $cursor
+ * @property string $direction
+ * @property string $display
+ * @property string $elevation
+ * @property string $empty_cells
+ * @property string $float
+ * @property string $font_family
+ * @property float $font_size Length in pt
+ * @property string $font_style
+ * @property string $font_variant
+ * @property string $font_weight
+ * @property float|string $height Length in pt, a percentage value, or `auto`
+ * @property string $image_resolution
+ * @property string $inset Only use for setting all box insets to the same length
+ * @property float|string $left Length in pt, a percentage value, or `auto`
+ * @property float $letter_spacing Length in pt
+ * @property float $line_height Length in pt
+ * @property string $list_style_image Image URL or `none`
+ * @property string $list_style_position
+ * @property string $list_style_type
+ * @property float|string $margin_right Length in pt, a percentage value, or `auto`
+ * @property float|string $margin_left Length in pt, a percentage value, or `auto`
+ * @property float|string $margin_top Length in pt, a percentage value, or `auto`
+ * @property float|string $margin_bottom Length in pt, a percentage value, or `auto`
+ * @property string $margin Only use for setting all sides to the same length
+ * @property float|string $max_height Length in pt, a percentage value, or `none`
+ * @property float|string $max_width Length in pt, a percentage value, or `none`
+ * @property float|string $min_height Length in pt, a percentage value, or `auto`
+ * @property float|string $min_width Length in pt, a percentage value, or `auto`
+ * @property float $opacity Number in the range [0, 1]
+ * @property int $orphans
+ * @property array|string $outline_color
+ * @property string $outline_style Valid border style, except for `hidden`
+ * @property float $outline_width Length in pt
+ * @property float $outline_offset Length in pt
+ * @property string $overflow
+ * @property string $overflow_wrap
+ * @property float|string $padding_top Length in pt or a percentage value
+ * @property float|string $padding_right Length in pt or a percentage value
+ * @property float|string $padding_bottom Length in pt or a percentage value
+ * @property float|string $padding_left Length in pt or a percentage value
+ * @property string $padding Only use for setting all sides to the same length
+ * @property string $page_break_after
+ * @property string $page_break_before
+ * @property string $page_break_inside
+ * @property string $pause_after
+ * @property string $pause_before
+ * @property string $pause
+ * @property string $pitch_range
+ * @property string $pitch
+ * @property string $play_during
+ * @property string $position
+ * @property string $quotes
+ * @property string $richness
+ * @property float|string $right Length in pt, a percentage value, or `auto`
+ * @property float[]|string $size Pair of `[width, height]` or `auto`
+ * @property string $speak_header
+ * @property string $speak_numeral
+ * @property string $speak_punctuation
+ * @property string $speak
+ * @property string $speech_rate
+ * @property string $src
+ * @property string $stress
+ * @property string $table_layout
+ * @property string $text_align
+ * @property string $text_decoration
+ * @property float|string $text_indent Length in pt or a percentage value
+ * @property string $text_transform
+ * @property float|string $top Length in pt, a percentage value, or `auto`
+ * @property array $transform List of transforms
+ * @property array $transform_origin
+ * @property string $unicode_bidi
+ * @property string $unicode_range
+ * @property string $vertical_align
+ * @property string $visibility
+ * @property string $voice_family
+ * @property string $volume
+ * @property string $white_space
+ * @property int $widows
+ * @property float|string $width Length in pt, a percentage value, or `auto`
+ * @property string $word_break
+ * @property float $word_spacing Length in pt
+ * @property int|string $z_index Integer value or `auto`
+ * @property string $_dompdf_keep
+ *
+ * @package dompdf
+ */
+class Style
+{
+ protected const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
+ protected const CSS_INTEGER = "[+-]?\d+";
+ protected const CSS_NUMBER = "[+-]?\d*\.?\d+(?:[eE][+-]?\d+)?";
+
+ /**
+ * Default font size, in points.
+ *
+ * @var float
+ */
+ public static $default_font_size = 12;
+
+ /**
+ * Default line height, as a fraction of the font size.
+ *
+ * @var float
+ */
+ public static $default_line_height = 1.2;
+
+ /**
+ * Default "absolute" font sizes relative to the default font-size
+ * https://www.w3.org/TR/css-fonts-3/#absolute-size-value
+ *
+ * @var array
+ */
+ public static $font_size_keywords = [
+ "xx-small" => 0.6, // 3/5
+ "x-small" => 0.75, // 3/4
+ "small" => 0.889, // 8/9
+ "medium" => 1, // 1
+ "large" => 1.2, // 6/5
+ "x-large" => 1.5, // 3/2
+ "xx-large" => 2.0, // 2/1
+ ];
+
+ /**
+ * List of valid text-align keywords.
+ */
+ public const TEXT_ALIGN_KEYWORDS = ["left", "right", "center", "justify"];
+
+ /**
+ * List of valid vertical-align keywords.
+ */
+ public const VERTICAL_ALIGN_KEYWORDS = ["baseline", "bottom", "middle",
+ "sub", "super", "text-bottom", "text-top", "top"];
+
+ /**
+ * List of all block-level (outer) display types.
+ * * https://www.w3.org/TR/css-display-3/#display-type
+ * * https://www.w3.org/TR/css-display-3/#block-level
+ */
+ public const BLOCK_LEVEL_TYPES = [
+ "block",
+ // "flow-root",
+ "list-item",
+ // "flex",
+ // "grid",
+ "table"
+ ];
+
+ /**
+ * List of all inline-level (outer) display types.
+ * * https://www.w3.org/TR/css-display-3/#display-type
+ * * https://www.w3.org/TR/css-display-3/#inline-level
+ */
+ public const INLINE_LEVEL_TYPES = [
+ "inline",
+ "inline-block",
+ // "inline-flex",
+ // "inline-grid",
+ "inline-table"
+ ];
+
+ /**
+ * List of all table-internal (outer) display types.
+ * * https://www.w3.org/TR/css-display-3/#layout-specific-display
+ */
+ public const TABLE_INTERNAL_TYPES = [
+ "table-row-group",
+ "table-header-group",
+ "table-footer-group",
+ "table-row",
+ "table-cell",
+ "table-column-group",
+ "table-column",
+ "table-caption"
+ ];
+
+ /**
+ * List of all inline (inner) display types.
+ */
+ public const INLINE_TYPES = ["inline"];
+
+ /**
+ * List of all block (inner) display types.
+ */
+ public const BLOCK_TYPES = ["block", "inline-block", "table-cell", "list-item"];
+
+ /**
+ * List of all table (inner) display types.
+ */
+ public const TABLE_TYPES = ["table", "inline-table"];
+
+ /**
+ * Lookup table for valid display types. Initially computed from the
+ * different constants.
+ *
+ * @var array
+ */
+ protected static $valid_display_types = [];
+
+ /**
+ * List of all positioned types.
+ */
+ public const POSITIONED_TYPES = ["relative", "absolute", "fixed"];
+
+ /**
+ * List of valid border styles.
+ */
+ public const BORDER_STYLES = [
+ "none", "hidden",
+ "dotted", "dashed", "solid",
+ "double", "groove", "ridge", "inset", "outset"
+ ];
+
+ /**
+ * List of valid outline-style values.
+ * Same as the border styles, except `auto` is allowed, `hidden` is not.
+ *
+ * @link https://www.w3.org/TR/css-ui-4/#typedef-outline-line-style
+ */
+ protected const OUTLINE_STYLES = [
+ "auto", "none",
+ "dotted", "dashed", "solid",
+ "double", "groove", "ridge", "inset", "outset"
+ ];
+
+ /**
+ * Map of CSS shorthand properties and their corresponding sub-properties.
+ * The order of the sub-properties is relevant for the fallback getter,
+ * which is used in case no specific getter method is defined.
+ *
+ * @var array
+ */
+ protected static $_props_shorthand = [
+ "background" => [
+ "background_image",
+ "background_position",
+ "background_size",
+ "background_repeat",
+ // "background_origin",
+ // "background_clip",
+ "background_attachment",
+ "background_color"
+ ],
+ "border" => [
+ "border_top_width",
+ "border_right_width",
+ "border_bottom_width",
+ "border_left_width",
+ "border_top_style",
+ "border_right_style",
+ "border_bottom_style",
+ "border_left_style",
+ "border_top_color",
+ "border_right_color",
+ "border_bottom_color",
+ "border_left_color"
+ ],
+ "border_top" => [
+ "border_top_width",
+ "border_top_style",
+ "border_top_color"
+ ],
+ "border_right" => [
+ "border_right_width",
+ "border_right_style",
+ "border_right_color"
+ ],
+ "border_bottom" => [
+ "border_bottom_width",
+ "border_bottom_style",
+ "border_bottom_color"
+ ],
+ "border_left" => [
+ "border_left_width",
+ "border_left_style",
+ "border_left_color"
+ ],
+ "border_width" => [
+ "border_top_width",
+ "border_right_width",
+ "border_bottom_width",
+ "border_left_width"
+ ],
+ "border_style" => [
+ "border_top_style",
+ "border_right_style",
+ "border_bottom_style",
+ "border_left_style"
+ ],
+ "border_color" => [
+ "border_top_color",
+ "border_right_color",
+ "border_bottom_color",
+ "border_left_color"
+ ],
+ "border_radius" => [
+ "border_top_left_radius",
+ "border_top_right_radius",
+ "border_bottom_right_radius",
+ "border_bottom_left_radius"
+ ],
+ "font" => [
+ "font_family",
+ "font_size",
+ // "font_stretch",
+ "font_style",
+ "font_variant",
+ "font_weight",
+ "line_height"
+ ],
+ "inset" => [
+ "top",
+ "right",
+ "bottom",
+ "left"
+ ],
+ "list_style" => [
+ "list_style_image",
+ "list_style_position",
+ "list_style_type"
+ ],
+ "margin" => [
+ "margin_top",
+ "margin_right",
+ "margin_bottom",
+ "margin_left"
+ ],
+ "padding" => [
+ "padding_top",
+ "padding_right",
+ "padding_bottom",
+ "padding_left"
+ ],
+ "outline" => [
+ "outline_width",
+ "outline_style",
+ "outline_color"
+ ]
+ ];
+
+ /**
+ * Maps legacy property names to actual property names.
+ *
+ * @var array
+ */
+ protected static $_props_alias = [
+ "word_wrap" => "overflow_wrap",
+ "_dompdf_background_image_resolution" => "background_image_resolution",
+ "_dompdf_image_resolution" => "image_resolution",
+ "_webkit_transform" => "transform",
+ "_webkit_transform_origin" => "transform_origin"
+ ];
+
+ /**
+ * Default style values.
+ *
+ * @link https://www.w3.org/TR/CSS21/propidx.html
+ *
+ * @var array
+ */
+ protected static $_defaults = null;
+
+ /**
+ * List of inherited properties
+ *
+ * @link https://www.w3.org/TR/CSS21/propidx.html
+ *
+ * @var array
+ */
+ protected static $_inherited = null;
+
+ /**
+ * Caches method_exists result
+ *
+ * @var array
+ */
+ protected static $_methods_cache = [];
+
+ /**
+ * The stylesheet this style belongs to
+ *
+ * @var Stylesheet
+ */
+ protected $_stylesheet;
+
+ /**
+ * Media queries attached to the style
+ *
+ * @var array
+ */
+ protected $_media_queries;
+
+ /**
+ * Properties set by an `!important` declaration.
+ *
+ * @var array
+ */
+ protected $_important_props = [];
+
+ /**
+ * Specified (or declared) values of the CSS properties.
+ *
+ * https://www.w3.org/TR/css-cascade-3/#value-stages
+ *
+ * @var array
+ */
+ protected $_props = [];
+
+ /**
+ * Computed values of the CSS properties.
+ *
+ * @var array
+ */
+ protected $_props_computed = [];
+
+ /**
+ * Used values of the CSS properties.
+ *
+ * @var array
+ */
+ protected $_props_used = [];
+
+ /**
+ * Marks properties with non-final used values that should be cleared on
+ * style reset.
+ *
+ * @var array
+ */
+ protected $non_final_used = [];
+
+ protected static $_dependency_map = [
+ "border_top_style" => [
+ "border_top_width"
+ ],
+ "border_bottom_style" => [
+ "border_bottom_width"
+ ],
+ "border_left_style" => [
+ "border_left_width"
+ ],
+ "border_right_style" => [
+ "border_right_width"
+ ],
+ "direction" => [
+ "text_align"
+ ],
+ "font_size" => [
+ "background_position",
+ "background_size",
+ "border_top_width",
+ "border_right_width",
+ "border_bottom_width",
+ "border_left_width",
+ "border_top_left_radius",
+ "border_top_right_radius",
+ "border_bottom_right_radius",
+ "border_bottom_left_radius",
+ "letter_spacing",
+ "line_height",
+ "margin_top",
+ "margin_right",
+ "margin_bottom",
+ "margin_left",
+ "outline_width",
+ "outline_offset",
+ "padding_top",
+ "padding_right",
+ "padding_bottom",
+ "padding_left",
+ "word_spacing",
+ "width",
+ "height",
+ "min-width",
+ "min-height",
+ "max-width",
+ "max-height"
+ ],
+ "float" => [
+ "display"
+ ],
+ "position" => [
+ "display"
+ ],
+ "outline_style" => [
+ "outline_width"
+ ]
+ ];
+
+ /**
+ * Lookup table for dependent properties. Initially computed from the
+ * dependency map.
+ *
+ * @var array
+ */
+ protected static $_dependent_props = [];
+
+ /**
+ * Style of the parent element in document tree.
+ *
+ * @var Style
+ */
+ protected $parent_style;
+
+ /**
+ * @var Frame|null
+ */
+ protected $_frame;
+
+ /**
+ * The origin of the style
+ *
+ * @var int
+ */
+ protected $_origin = Stylesheet::ORIG_AUTHOR;
+
+ /**
+ * The computed bottom spacing
+ *
+ * @var float|string|null
+ */
+ private $_computed_bottom_spacing = null;
+
+ /**
+ * @var bool|null
+ */
+ private $has_border_radius_cache = null;
+
+ /**
+ * @var array|null
+ */
+ private $resolved_border_radius = null;
+
+ /**
+ * @var FontMetrics
+ */
+ private $fontMetrics;
+
+ /**
+ * @param Stylesheet $stylesheet The stylesheet the style is associated with.
+ * @param int $origin
+ */
+ public function __construct(Stylesheet $stylesheet, int $origin = Stylesheet::ORIG_AUTHOR)
+ {
+ $this->fontMetrics = $stylesheet->getFontMetrics();
+
+ $this->_stylesheet = $stylesheet;
+ $this->_media_queries = [];
+ $this->_origin = $origin;
+ $this->parent_style = null;
+
+ if (!isset(self::$_defaults)) {
+
+ // Shorthand
+ $d =& self::$_defaults;
+
+ // All CSS 2.1 properties, and their default values
+ // Some properties are specified with their computed value for
+ // efficiency; this only works if the computed value is not
+ // dependent on another property
+ $d["azimuth"] = "center";
+ $d["background_attachment"] = "scroll";
+ $d["background_color"] = "transparent";
+ $d["background_image"] = "none";
+ $d["background_image_resolution"] = "normal";
+ $d["background_position"] = ["0%", "0%"];
+ $d["background_repeat"] = "repeat";
+ $d["background"] = "";
+ $d["border_collapse"] = "separate";
+ $d["border_color"] = "";
+ $d["border_spacing"] = [0.0, 0.0];
+ $d["border_style"] = "";
+ $d["border_top"] = "";
+ $d["border_right"] = "";
+ $d["border_bottom"] = "";
+ $d["border_left"] = "";
+ $d["border_top_color"] = "currentcolor";
+ $d["border_right_color"] = "currentcolor";
+ $d["border_bottom_color"] = "currentcolor";
+ $d["border_left_color"] = "currentcolor";
+ $d["border_top_style"] = "none";
+ $d["border_right_style"] = "none";
+ $d["border_bottom_style"] = "none";
+ $d["border_left_style"] = "none";
+ $d["border_top_width"] = "medium";
+ $d["border_right_width"] = "medium";
+ $d["border_bottom_width"] = "medium";
+ $d["border_left_width"] = "medium";
+ $d["border_width"] = "";
+ $d["border_bottom_left_radius"] = 0.0;
+ $d["border_bottom_right_radius"] = 0.0;
+ $d["border_top_left_radius"] = 0.0;
+ $d["border_top_right_radius"] = 0.0;
+ $d["border_radius"] = "";
+ $d["border"] = "";
+ $d["bottom"] = "auto";
+ $d["caption_side"] = "top";
+ $d["clear"] = "none";
+ $d["clip"] = "auto";
+ $d["color"] = "#000000";
+ $d["content"] = "normal";
+ $d["counter_increment"] = "none";
+ $d["counter_reset"] = "none";
+ $d["cue_after"] = "none";
+ $d["cue_before"] = "none";
+ $d["cue"] = "";
+ $d["cursor"] = "auto";
+ $d["direction"] = "ltr";
+ $d["display"] = "inline";
+ $d["elevation"] = "level";
+ $d["empty_cells"] = "show";
+ $d["float"] = "none";
+ $d["font_family"] = $stylesheet->get_dompdf()->getOptions()->getDefaultFont();
+ $d["font_size"] = "medium";
+ $d["font_style"] = "normal";
+ $d["font_variant"] = "normal";
+ $d["font_weight"] = "normal";
+ $d["font"] = "";
+ $d["height"] = "auto";
+ $d["image_resolution"] = "normal";
+ $d["inset"] = "";
+ $d["left"] = "auto";
+ $d["letter_spacing"] = "normal";
+ $d["line_height"] = "normal";
+ $d["list_style_image"] = "none";
+ $d["list_style_position"] = "outside";
+ $d["list_style_type"] = "disc";
+ $d["list_style"] = "";
+ $d["margin_right"] = 0.0;
+ $d["margin_left"] = 0.0;
+ $d["margin_top"] = 0.0;
+ $d["margin_bottom"] = 0.0;
+ $d["margin"] = "";
+ $d["max_height"] = "none";
+ $d["max_width"] = "none";
+ $d["min_height"] = "auto";
+ $d["min_width"] = "auto";
+ $d["orphans"] = 2;
+ $d["outline_color"] = "currentcolor"; // "invert" special color is not supported
+ $d["outline_style"] = "none";
+ $d["outline_width"] = "medium";
+ $d["outline_offset"] = 0.0;
+ $d["outline"] = "";
+ $d["overflow"] = "visible";
+ $d["overflow_wrap"] = "normal";
+ $d["padding_top"] = 0.0;
+ $d["padding_right"] = 0.0;
+ $d["padding_bottom"] = 0.0;
+ $d["padding_left"] = 0.0;
+ $d["padding"] = "";
+ $d["page_break_after"] = "auto";
+ $d["page_break_before"] = "auto";
+ $d["page_break_inside"] = "auto";
+ $d["pause_after"] = "0";
+ $d["pause_before"] = "0";
+ $d["pause"] = "";
+ $d["pitch_range"] = "50";
+ $d["pitch"] = "medium";
+ $d["play_during"] = "auto";
+ $d["position"] = "static";
+ $d["quotes"] = "auto";
+ $d["richness"] = "50";
+ $d["right"] = "auto";
+ $d["size"] = "auto"; // @page
+ $d["speak_header"] = "once";
+ $d["speak_numeral"] = "continuous";
+ $d["speak_punctuation"] = "none";
+ $d["speak"] = "normal";
+ $d["speech_rate"] = "medium";
+ $d["stress"] = "50";
+ $d["table_layout"] = "auto";
+ $d["text_align"] = "";
+ $d["text_decoration"] = "none";
+ $d["text_indent"] = 0.0;
+ $d["text_transform"] = "none";
+ $d["top"] = "auto";
+ $d["unicode_bidi"] = "normal";
+ $d["vertical_align"] = "baseline";
+ $d["visibility"] = "visible";
+ $d["voice_family"] = "";
+ $d["volume"] = "medium";
+ $d["white_space"] = "normal";
+ $d["widows"] = 2;
+ $d["width"] = "auto";
+ $d["word_break"] = "normal";
+ $d["word_spacing"] = "normal";
+ $d["z_index"] = "auto";
+
+ // CSS3
+ $d["opacity"] = 1.0;
+ $d["background_size"] = ["auto", "auto"];
+ $d["transform"] = "none";
+ $d["transform_origin"] = "50% 50%";
+
+ // for @font-face
+ $d["src"] = "";
+ $d["unicode_range"] = "";
+
+ // vendor-prefixed properties
+ $d["_dompdf_keep"] = "";
+
+ // Properties that inherit by default
+ self::$_inherited = [
+ "azimuth",
+ "background_image_resolution",
+ "border_collapse",
+ "border_spacing",
+ "caption_side",
+ "color",
+ "cursor",
+ "direction",
+ "elevation",
+ "empty_cells",
+ "font_family",
+ "font_size",
+ "font_style",
+ "font_variant",
+ "font_weight",
+ "font",
+ "image_resolution",
+ "letter_spacing",
+ "line_height",
+ "list_style_image",
+ "list_style_position",
+ "list_style_type",
+ "list_style",
+ "orphans",
+ "overflow_wrap",
+ "pitch_range",
+ "pitch",
+ "quotes",
+ "richness",
+ "speak_header",
+ "speak_numeral",
+ "speak_punctuation",
+ "speak",
+ "speech_rate",
+ "stress",
+ "text_align",
+ "text_indent",
+ "text_transform",
+ "visibility",
+ "voice_family",
+ "volume",
+ "white_space",
+ "widows",
+ "word_break",
+ "word_spacing",
+ ];
+
+ // Compute dependent props from dependency map
+ foreach (self::$_dependency_map as $props) {
+ foreach ($props as $prop) {
+ self::$_dependent_props[$prop] = true;
+ }
+ }
+
+ // Compute valid display-type lookup table
+ self::$valid_display_types = [
+ "none" => true,
+ "-dompdf-br" => true,
+ "-dompdf-image" => true,
+ "-dompdf-list-bullet" => true,
+ "-dompdf-page" => true
+ ];
+ foreach (self::BLOCK_LEVEL_TYPES as $val) {
+ self::$valid_display_types[$val] = true;
+ }
+ foreach (self::INLINE_LEVEL_TYPES as $val) {
+ self::$valid_display_types[$val] = true;
+ }
+ foreach (self::TABLE_INTERNAL_TYPES as $val) {
+ self::$valid_display_types[$val] = true;
+ }
+ }
+ }
+
+ /**
+ * Clear all non-final used values.
+ *
+ * @return void
+ */
+ public function reset(): void
+ {
+ foreach (array_keys($this->non_final_used) as $prop) {
+ unset($this->_props_used[$prop]);
+ }
+
+ $this->non_final_used = [];
+ }
+
+ /**
+ * @param array $media_queries
+ */
+ public function set_media_queries(array $media_queries): void
+ {
+ $this->_media_queries = $media_queries;
+ }
+
+ /**
+ * @return array
+ */
+ public function get_media_queries(): array
+ {
+ return $this->_media_queries;
+ }
+
+ /**
+ * @param Frame $frame
+ */
+ public function set_frame(Frame $frame): void
+ {
+ $this->_frame = $frame;
+ }
+
+ /**
+ * @return Frame|null
+ */
+ public function get_frame(): ?Frame
+ {
+ return $this->_frame;
+ }
+
+ /**
+ * @param int $origin
+ */
+ public function set_origin(int $origin): void
+ {
+ $this->_origin = $origin;
+ }
+
+ /**
+ * @return int
+ */
+ public function get_origin(): int
+ {
+ return $this->_origin;
+ }
+
+ /**
+ * Returns the {@link Stylesheet} the style is associated with.
+ *
+ * @return Stylesheet
+ */
+ public function get_stylesheet(): Stylesheet
+ {
+ return $this->_stylesheet;
+ }
+
+ public function is_absolute(): bool
+ {
+ $position = $this->__get("position");
+ return $position === "absolute" || $position === "fixed";
+ }
+
+ public function is_in_flow(): bool
+ {
+ $float = $this->__get("float");
+ return $float === "none" && !$this->is_absolute();
+ }
+
+ /**
+ * Converts any CSS length value into an absolute length in points.
+ *
+ * length_in_pt() takes a single length (e.g. '1em') or an array of
+ * lengths and returns an absolute length. If an array is passed, then
+ * the return value is the sum of all elements. If any of the lengths
+ * provided are "auto" or "none" then that value is returned.
+ *
+ * If a reference size is not provided, the current font size is used.
+ *
+ * @param float|string|array $length The numeric length (or string measurement) or array of lengths to resolve.
+ * @param float|null $ref_size An absolute reference size to resolve percentage lengths.
+ *
+ * @return float|string
+ */
+ public function length_in_pt($length, ?float $ref_size = null)
+ {
+ $font_size = $this->__get("font_size");
+ $ref_size = $ref_size ?? $font_size;
+
+ if (!\is_array($length)) {
+ $length = [$length];
+ }
+
+ $ret = 0.0;
+
+ foreach ($length as $l) {
+ if ($l === "auto" || $l === "none") {
+ return $l;
+ }
+
+ // Assume numeric values are already in points
+ if (is_numeric($l)) {
+ $ret += (float) $l;
+ continue;
+ }
+
+ $val = $this->single_length_in_pt((string) $l, $ref_size, $font_size);
+ $ret += $val ?? 0;
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Convert a length declaration to pt.
+ *
+ * @param string $l The length declaration.
+ * @param float $ref_size Reference size for percentage declarations.
+ * @param float|null $font_size Font size for resolving font-size relative units.
+ *
+ * @return float|null The length in pt, or `null` for invalid declarations.
+ */
+ protected function single_length_in_pt(string $l, float $ref_size = 0, ?float $font_size = null): ?float
+ {
+ static $cache = [];
+
+ $font_size = $font_size ?? $this->__get("font_size");
+
+ $key = "$l/$ref_size/$font_size";
+
+ if (\array_key_exists($key, $cache)) {
+ return $cache[$key];
+ }
+
+ $number = self::CSS_NUMBER;
+ $pattern = "/^($number)(.*)?$/";
+
+ if (!preg_match($pattern, $l, $matches)) {
+ return null;
+ }
+
+ $v = (float) $matches[1];
+ $unit = mb_strtolower($matches[2]);
+
+ if ($unit === "") {
+ // Legacy support for unitless values, not covered by spec. Might
+ // want to restrict this to unitless `0` in the future
+ $value = $v;
+ }
+
+ elseif ($unit === "%") {
+ $value = $v / 100 * $ref_size;
+ }
+
+ elseif ($unit === "px") {
+ $dpi = $this->_stylesheet->get_dompdf()->getOptions()->getDpi();
+ $value = ($v * 72) / $dpi;
+ }
+
+ elseif ($unit === "pt") {
+ $value = $v;
+ }
+
+ elseif ($unit === "rem") {
+ $tree = $this->_stylesheet->get_dompdf()->getTree();
+ $root_style = $tree !== null ? $tree->get_root()->get_style() : null;
+ $root_font_size = $root_style === null || $root_style === $this
+ ? $font_size
+ : $root_style->__get("font_size");
+ $value = $v * $root_font_size;
+
+ // Skip caching if the root style is not available yet, as to avoid
+ // incorrectly cached values if the root font size is different from
+ // the default
+ if ($root_style === null) {
+ return $value;
+ }
+ }
+
+ elseif ($unit === "em") {
+ $value = $v * $font_size;
+ }
+
+ elseif ($unit === "cm") {
+ $value = $v * 72 / 2.54;
+ }
+
+ elseif ($unit === "mm") {
+ $value = $v * 72 / 25.4;
+ }
+
+ elseif ($unit === "ex") {
+ // FIXME: em:ex ratio?
+ $value = $v * $font_size / 2;
+ }
+
+ elseif ($unit === "in") {
+ $value = $v * 72;
+ }
+
+ elseif ($unit === "pc") {
+ $value = $v * 12;
+ }
+
+ else {
+ // Invalid or unsupported declaration
+ $value = null;
+ }
+
+ return $cache[$key] = $value;
+ }
+
+ /**
+ * Resolve inherited property values using the provided parent style or the
+ * default values, in case no parent style exists.
+ *
+ * https://www.w3.org/TR/css-cascade-3/#inheriting
+ *
+ * @param Style|null $parent
+ */
+ public function inherit(?Style $parent = null): void
+ {
+ $this->parent_style = $parent;
+
+ // Clear the computed font size, as it might depend on the parent
+ // font size
+ unset($this->_props_computed["font_size"]);
+ unset($this->_props_used["font_size"]);
+
+ if ($parent) {
+ foreach (self::$_inherited as $prop) {
+ // For properties that inherit by default: When the cascade did
+ // not result in a value, inherit the parent value. Inheritance
+ // is handled via the specific sub-properties for shorthands
+ if (isset($this->_props[$prop]) || isset(self::$_props_shorthand[$prop])) {
+ continue;
+ }
+
+ if (isset($parent->_props[$prop])) {
+ $parent_val = $parent->computed($prop);
+
+ $this->_props[$prop] = $parent_val;
+ $this->_props_computed[$prop] = $parent_val;
+ $this->_props_used[$prop] = null;
+ }
+ }
+ }
+
+ foreach ($this->_props as $prop => $val) {
+ if ($val === "inherit") {
+ if ($parent && isset($parent->_props[$prop])) {
+ $parent_val = $parent->computed($prop);
+
+ $this->_props[$prop] = $parent_val;
+ $this->_props_computed[$prop] = $parent_val;
+ $this->_props_used[$prop] = null;
+ } else {
+ // Parent prop not set, use default
+ $this->_props[$prop] = self::$_defaults[$prop];
+ unset($this->_props_computed[$prop]);
+ unset($this->_props_used[$prop]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Override properties in this style with those in $style
+ *
+ * @param Style $style
+ */
+ public function merge(Style $style): void
+ {
+ foreach ($style->_props as $prop => $val) {
+ $important = isset($style->_important_props[$prop]);
+
+ // `!important` declarations take precedence over normal ones
+ if (!$important && isset($this->_important_props[$prop])) {
+ continue;
+ }
+
+ if ($important) {
+ $this->_important_props[$prop] = true;
+ }
+
+ $this->_props[$prop] = $val;
+
+ // Copy an existing computed value only for non-dependent
+ // properties; otherwise it may be invalid for the current style
+ if (!isset(self::$_dependent_props[$prop])
+ && \array_key_exists($prop, $style->_props_computed)
+ ) {
+ $this->_props_computed[$prop] = $style->_props_computed[$prop];
+ $this->_props_used[$prop] = null;
+ } else {
+ unset($this->_props_computed[$prop]);
+ unset($this->_props_used[$prop]);
+ }
+ }
+ }
+
+ /**
+ * Clear information about important declarations after the style has been
+ * finalized during stylesheet loading.
+ */
+ public function clear_important(): void
+ {
+ $this->_important_props = [];
+ }
+
+ /**
+ * Clear border-radius and bottom-spacing cache as necessary when a given
+ * property is set.
+ *
+ * @param string $prop The property that is set.
+ */
+ protected function clear_cache(string $prop): void
+ {
+ // Clear border-radius cache on setting any border-radius
+ // property
+ if ($prop === "border_top_left_radius"
+ || $prop === "border_top_right_radius"
+ || $prop === "border_bottom_left_radius"
+ || $prop === "border_bottom_right_radius"
+ ) {
+ $this->has_border_radius_cache = null;
+ $this->resolved_border_radius = null;
+ }
+
+ // Clear bottom-spacing cache if necessary. Border style can
+ // disable/enable border calculations
+ if ($prop === "margin_bottom"
+ || $prop === "padding_bottom"
+ || $prop === "border_bottom_width"
+ || $prop === "border_bottom_style"
+ ) {
+ $this->_computed_bottom_spacing = null;
+ }
+ }
+
+ /**
+ * Set a style property from a value declaration.
+ *
+ * Setting `$clear_dependencies` to `false` is useful for saving a bit of
+ * unnecessary work while loading stylesheets.
+ *
+ * @param string $prop The property to set.
+ * @param mixed $val The value declaration or computed value.
+ * @param bool $important Whether the declaration is important.
+ * @param bool $clear_dependencies Whether to clear computed values of dependent properties.
+ */
+ public function set_prop(string $prop, $val, bool $important = false, bool $clear_dependencies = true): void
+ {
+ $prop = str_replace("-", "_", $prop);
+
+ // Legacy property aliases
+ if (isset(self::$_props_alias[$prop])) {
+ $prop = self::$_props_alias[$prop];
+ }
+
+ if (!isset(self::$_defaults[$prop])) {
+ global $_dompdf_warnings;
+ $_dompdf_warnings[] = "'$prop' is not a recognized CSS property.";
+ return;
+ }
+
+ if ($prop !== "content" && \is_string($val) && mb_strpos($val, "url") === false && mb_strlen($val) > 1) {
+ $val = mb_strtolower(trim(str_replace(["\n", "\t"], [" "], $val)));
+ }
+
+ if (isset(self::$_props_shorthand[$prop])) {
+ // Shorthand properties directly set their respective sub-properties
+ // https://www.w3.org/TR/css-cascade-3/#shorthand
+ if ($val === "initial" || $val === "inherit" || $val === "unset") {
+ foreach (self::$_props_shorthand[$prop] as $sub_prop) {
+ $this->set_prop($sub_prop, $val, $important, $clear_dependencies);
+ }
+ } else {
+ $method = "_set_$prop";
+
+ if (!isset(self::$_methods_cache[$method])) {
+ self::$_methods_cache[$method] = method_exists($this, $method);
+ }
+
+ if (self::$_methods_cache[$method]) {
+ $values = $this->$method($val);
+
+ if ($values === []) {
+ return;
+ }
+
+ // Each missing sub-property is assigned its initial value
+ // https://www.w3.org/TR/css-cascade-3/#shorthand
+ foreach (self::$_props_shorthand[$prop] as $sub_prop) {
+ $sub_val = $values[$sub_prop] ?? self::$_defaults[$sub_prop];
+ $this->set_prop($sub_prop, $sub_val, $important, $clear_dependencies);
+ }
+ }
+ }
+ } else {
+ // Legacy support for `word-break: break-word`
+ // https://www.w3.org/TR/css-text-3/#valdef-word-break-break-word
+ if ($prop === "word_break" && $val === "break-word") {
+ $val = "normal";
+ $this->set_prop("overflow_wrap", "anywhere", $important, $clear_dependencies);
+ }
+
+ // `!important` declarations take precedence over normal ones
+ if (!$important && isset($this->_important_props[$prop])) {
+ return;
+ }
+
+ if ($important) {
+ $this->_important_props[$prop] = true;
+ }
+
+ // https://www.w3.org/TR/css-cascade-3/#inherit-initial
+ if ($val === "unset") {
+ $val = \in_array($prop, self::$_inherited, true)
+ ? "inherit"
+ : "initial";
+ }
+
+ // https://www.w3.org/TR/css-cascade-3/#valdef-all-initial
+ if ($val === "initial") {
+ $val = self::$_defaults[$prop];
+ }
+
+ $computed = $this->compute_prop($prop, $val);
+
+ // Skip invalid declarations
+ if ($computed === null) {
+ return;
+ }
+
+ $this->_props[$prop] = $val;
+ $this->_props_computed[$prop] = $computed;
+ $this->_props_used[$prop] = null;
+
+ if ($clear_dependencies) {
+ // Clear the computed values of any dependent properties, so
+ // they can be re-computed
+ if (isset(self::$_dependency_map[$prop])) {
+ foreach (self::$_dependency_map[$prop] as $dependent) {
+ unset($this->_props_computed[$dependent]);
+ unset($this->_props_used[$dependent]);
+ }
+ }
+
+ $this->clear_cache($prop);
+ }
+ }
+ }
+
+ /**
+ * Get the specified value of a style property.
+ *
+ * @param string $prop
+ *
+ * @return mixed
+ * @throws Exception
+ */
+ public function get_specified(string $prop)
+ {
+ // Legacy property aliases
+ if (isset(self::$_props_alias[$prop])) {
+ $prop = self::$_props_alias[$prop];
+ }
+
+ if (!isset(self::$_defaults[$prop])) {
+ throw new Exception("'$prop' is not a recognized CSS property.");
+ }
+
+ return $this->_props[$prop] ?? self::$_defaults[$prop];
+ }
+
+ /**
+ * Set a style property to its final value.
+ *
+ * This sets the specified and used value of the style property to the given
+ * value, meaning the value is not parsed and thus should have a type
+ * compatible with the property.
+ *
+ * If a shorthand property is specified, all of its sub-properties are set
+ * to the given value.
+ *
+ * @param string $prop The property to set.
+ * @param mixed $val The final value of the property.
+ *
+ * @throws Exception
+ */
+ public function __set(string $prop, $val)
+ {
+ // Legacy property aliases
+ if (isset(self::$_props_alias[$prop])) {
+ $prop = self::$_props_alias[$prop];
+ }
+
+ if (!isset(self::$_defaults[$prop])) {
+ throw new Exception("'$prop' is not a recognized CSS property.");
+ }
+
+ if (isset(self::$_props_shorthand[$prop])) {
+ foreach (self::$_props_shorthand[$prop] as $sub_prop) {
+ $this->__set($sub_prop, $val);
+ }
+ } else {
+ $this->_props[$prop] = $val;
+ $this->_props_computed[$prop] = $val;
+ $this->_props_used[$prop] = $val;
+
+ $this->clear_cache($prop);
+ }
+ }
+
+ /**
+ * Set the used value of a style property.
+ *
+ * Used values are cleared on style reset.
+ *
+ * If a shorthand property is specified, all of its sub-properties are set
+ * to the given value.
+ *
+ * @param string $prop The property to set.
+ * @param mixed $val The used value of the property.
+ *
+ * @throws Exception
+ */
+ public function set_used(string $prop, $val): void
+ {
+ // Legacy property aliases
+ if (isset(self::$_props_alias[$prop])) {
+ $prop = self::$_props_alias[$prop];
+ }
+
+ if (!isset(self::$_defaults[$prop])) {
+ throw new Exception("'$prop' is not a recognized CSS property.");
+ }
+
+ if (isset(self::$_props_shorthand[$prop])) {
+ foreach (self::$_props_shorthand[$prop] as $sub_prop) {
+ $this->set_used($sub_prop, $val);
+ }
+ } else {
+ $this->_props_used[$prop] = $val;
+ $this->non_final_used[$prop] = true;
+ }
+ }
+
+ /**
+ * Get the used or computed value of a style property, depending on whether
+ * the used value has been determined yet.
+ *
+ * @param string $prop
+ *
+ * @return mixed
+ * @throws Exception
+ */
+ public function __get(string $prop)
+ {
+ // Legacy property aliases
+ if (isset(self::$_props_alias[$prop])) {
+ $prop = self::$_props_alias[$prop];
+ }
+
+ if (!isset(self::$_defaults[$prop])) {
+ throw new Exception("'$prop' is not a recognized CSS property.");
+ }
+
+ if (isset($this->_props_used[$prop])) {
+ return $this->_props_used[$prop];
+ }
+
+ $method = "_get_$prop";
+
+ if (!isset(self::$_methods_cache[$method])) {
+ self::$_methods_cache[$method] = method_exists($this, $method);
+ }
+
+ if (isset(self::$_props_shorthand[$prop])) {
+ // Don't cache shorthand values, always use getter. If no dedicated
+ // getter exists, use a simple fallback getter concatenating all
+ // sub-property values
+ if (self::$_methods_cache[$method]) {
+ return $this->$method();
+ } else {
+ return implode(" ", array_map(function ($sub_prop) {
+ $val = $this->__get($sub_prop);
+ return \is_array($val) ? implode(" ", $val) : $val;
+ }, self::$_props_shorthand[$prop]));
+ }
+ } else {
+ $computed = $this->computed($prop);
+ $used = self::$_methods_cache[$method]
+ ? $this->$method($computed)
+ : $computed;
+
+ $this->_props_used[$prop] = $used;
+ return $used;
+ }
+ }
+
+ /**
+ * @param string $prop The property to compute.
+ * @param mixed $val The value to compute. Non-string values are treated as already computed.
+ *
+ * @return mixed The computed value.
+ */
+ protected function compute_prop(string $prop, $val)
+ {
+ // During style merge, the parent style is not available yet, so
+ // temporarily use the initial value for `inherit` properties. The
+ // keyword is properly resolved during inheritance
+ if ($val === "inherit") {
+ $val = self::$_defaults[$prop];
+ }
+
+ // Check for values which are already computed
+ if (!\is_string($val)) {
+ return $val;
+ }
+
+ $method = "_compute_$prop";
+
+ if (!isset(self::$_methods_cache[$method])) {
+ self::$_methods_cache[$method] = method_exists($this, $method);
+ }
+
+ if (self::$_methods_cache[$method]) {
+ return $this->$method($val);
+ } elseif ($val !== "") {
+ return $val;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the computed value for the given property.
+ *
+ * @param string $prop The property to get the computed value of.
+ *
+ * @return mixed The computed value.
+ */
+ protected function computed(string $prop)
+ {
+ if (!\array_key_exists($prop, $this->_props_computed)) {
+ $val = $this->_props[$prop] ?? self::$_defaults[$prop];
+ $computed = $this->compute_prop($prop, $val);
+
+ $this->_props_computed[$prop] = $computed;
+ }
+
+ return $this->_props_computed[$prop];
+ }
+
+ /**
+ * @param float $cbw The width of the containing block.
+ * @return float|string|null
+ */
+ public function computed_bottom_spacing(float $cbw)
+ {
+ // Caching the bottom spacing independently of the given width is a bit
+ // iffy, but should be okay, as the containing block should only
+ // potentially change after a page break, and the style is reset in that
+ // case
+ if ($this->_computed_bottom_spacing !== null) {
+ return $this->_computed_bottom_spacing;
+ }
+ return $this->_computed_bottom_spacing = $this->length_in_pt(
+ [
+ $this->margin_bottom,
+ $this->padding_bottom,
+ $this->border_bottom_width
+ ],
+ $cbw
+ );
+ }
+
+ /**
+ * Returns an `array(r, g, b, "r" => r, "g" => g, "b" => b, "alpha" => alpha, "hex" => "#rrggbb")`
+ * based on the provided CSS color value.
+ *
+ * @param string|null $color
+ * @return array|string|null
+ */
+ public function munge_color($color)
+ {
+ return Color::parse($color);
+ }
+
+ /**
+ * @return string
+ */
+ public function get_font_family_raw(): string
+ {
+ return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
+ }
+
+ /**
+ * Getter for the `font-family` CSS property.
+ *
+ * Uses the {@link FontMetrics} class to resolve the font family into an
+ * actual font file.
+ *
+ * @param string $computed
+ * @return string
+ * @throws Exception
+ *
+ * @link https://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
+ */
+ protected function _get_font_family($computed): string
+ {
+ //TODO: we should be using the calculated prop rather than perform the entire family parsing operation again
+
+ $fontMetrics = $this->getFontMetrics();
+ $DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss();
+
+ // Select the appropriate font. First determine the subtype, then check
+ // the specified font-families for a candidate.
+
+ // Resolve font-weight
+ $weight = $this->__get("font_weight");
+ if ($weight === 'bold') {
+ $weight = 700;
+ } elseif (preg_match('/^[0-9]+$/', $weight, $match)) {
+ $weight = (int)$match[0];
+ } else {
+ $weight = 400;
+ }
+
+ // Resolve font-style
+ $font_style = $this->__get("font_style");
+ $subtype = $fontMetrics->getType($weight . ' ' . $font_style);
+
+ $families = preg_split("/\s*,\s*/", $computed);
+
+ $font = null;
+ foreach ($families as $family) {
+ //remove leading and trailing string delimiters, e.g. on font names with spaces;
+ //remove leading and trailing whitespace
+ $family = trim($family, " \t\n\r\x0B\"'");
+ if ($DEBUGCSS) {
+ print '(' . $family . ')';
+ }
+ $font = $fontMetrics->getFont($family, $subtype);
+
+ if ($font) {
+ if ($DEBUGCSS) {
+ print "[get_font_family:";
+ print '(' . $computed . '.' . $font_style . '.' . $weight . '.' . $subtype . ')';
+ print '(' . $font . ")get_font_family]\n ";
+ }
+ return $font;
+ }
+ }
+
+ $family = null;
+ if ($DEBUGCSS) {
+ print '(default)';
+ }
+ $font = $fontMetrics->getFont($family, $subtype);
+
+ if ($font) {
+ if ($DEBUGCSS) {
+ print '(' . $font . ")get_font_family]\n";
+ }
+ return $font;
+ }
+
+ throw new Exception("Unable to find a suitable font replacement for: '" . $computed . "'");
+ }
+
+ /**
+ * @param float|string $computed
+ * @return float
+ *
+ * @link https://www.w3.org/TR/css-text-4/#word-spacing-property
+ */
+ protected function _get_word_spacing($computed)
+ {
+ if (\is_float($computed)) {
+ return $computed;
+ }
+
+ // Resolve percentage values
+ $font_size = $this->__get("font_size");
+ return $this->single_length_in_pt($computed, $font_size);
+ }
+
+ /**
+ * @param float|string $computed
+ * @return float
+ *
+ * @link https://www.w3.org/TR/css-text-4/#letter-spacing-property
+ */
+ protected function _get_letter_spacing($computed)
+ {
+ if (\is_float($computed)) {
+ return $computed;
+ }
+
+ // Resolve percentage values
+ $font_size = $this->__get("font_size");
+ return $this->single_length_in_pt($computed, $font_size);
+ }
+
+ /**
+ * @param float|string $computed
+ * @return float
+ *
+ * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
+ */
+ protected function _get_line_height($computed)
+ {
+ // Lengths have been computed to float, number values to string
+ if (\is_float($computed)) {
+ return $computed;
+ }
+
+ $font_size = $this->__get("font_size");
+ $factor = $computed === "normal"
+ ? self::$default_line_height
+ : (float) $computed;
+
+ return $factor * $font_size;
+ }
+
+ /**
+ * @param string $computed
+ * @param bool $current_is_parent
+ *
+ * @return array|string
+ */
+ protected function get_color_value($computed, bool $current_is_parent = false)
+ {
+ if ($computed === "currentcolor") {
+ // https://www.w3.org/TR/css-color-4/#resolving-other-colors
+ if ($current_is_parent) {
+ // Use the `color` value from the parent for the `color`
+ // property itself
+ return isset($this->parent_style)
+ ? $this->parent_style->__get("color")
+ : $this->munge_color(self::$_defaults["color"]);
+ }
+
+ return $this->__get("color");
+ }
+
+ return $this->munge_color($computed) ?? "transparent";
+ }
+
+ /**
+ * Returns the color as an array
+ *
+ * The array has the following format:
+ * `array(r, g, b, "r" => r, "g" => g, "b" => b, "alpha" => alpha, "hex" => "#rrggbb")`
+ *
+ * @param string $computed
+ * @return array|string
+ *
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-color
+ */
+ protected function _get_color($computed)
+ {
+ return $this->get_color_value($computed, true);
+ }
+
+ /**
+ * Returns the background color as an array
+ *
+ * See {@link Style::_get_color()} for format of the color array.
+ *
+ * @param string $computed
+ * @return array|string
+ *
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-color
+ */
+ protected function _get_background_color($computed)
+ {
+ return $this->get_color_value($computed);
+ }
+
+ /**
+ * Returns the background image URI, or "none"
+ *
+ * @param string $computed
+ * @return string
+ *
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
+ */
+ protected function _get_background_image($computed): string
+ {
+ return $this->_stylesheet->resolve_url($computed);
+ }
+
+ /**
+ * Returns the border color as an array
+ *
+ * See {@link Style::_get_color()} for format of the color array.
+ *
+ * @param string $computed
+ * @return array|string
+ *
+ * @link https://www.w3.org/TR/CSS21/box.html#border-color-properties
+ */
+ protected function _get_border_top_color($computed)
+ {
+ return $this->get_color_value($computed);
+ }
+
+ /**
+ * @param string $computed
+ * @return array|string
+ */
+ protected function _get_border_right_color($computed)
+ {
+ return $this->get_color_value($computed);
+ }
+
+ /**
+ * @param string $computed
+ * @return array|string
+ */
+ protected function _get_border_bottom_color($computed)
+ {
+ return $this->get_color_value($computed);
+ }
+
+ /**
+ * @param string $computed
+ * @return array|string
+ */
+ protected function _get_border_left_color($computed)
+ {
+ return $this->get_color_value($computed);
+ }
+
+ /**
+ * Return an array of all border properties.
+ *
+ * The returned array has the following structure:
+ *
+ * ```
+ * array("top" => array("width" => [border-width],
+ * "style" => [border-style],
+ * "color" => [border-color (array)]),
+ * "bottom" ... )
+ * ```
+ *
+ * @return array
+ */
+ public function get_border_properties(): array
+ {
+ return [
+ "top" => [
+ "width" => $this->__get("border_top_width"),
+ "style" => $this->__get("border_top_style"),
+ "color" => $this->__get("border_top_color"),
+ ],
+ "bottom" => [
+ "width" => $this->__get("border_bottom_width"),
+ "style" => $this->__get("border_bottom_style"),
+ "color" => $this->__get("border_bottom_color"),
+ ],
+ "right" => [
+ "width" => $this->__get("border_right_width"),
+ "style" => $this->__get("border_right_style"),
+ "color" => $this->__get("border_right_color"),
+ ],
+ "left" => [
+ "width" => $this->__get("border_left_width"),
+ "style" => $this->__get("border_left_style"),
+ "color" => $this->__get("border_left_color"),
+ ],
+ ];
+ }
+
+ /**
+ * Return a single border-side property
+ *
+ * @param string $side
+ * @return string
+ */
+ protected function get_border_side(string $side): string
+ {
+ $color = $this->__get("border_{$side}_color");
+
+ return $this->__get("border_{$side}_width") . " " .
+ $this->__get("border_{$side}_style") . " " .
+ (\is_array($color) ? $color["hex"] : $color);
+ }
+
+ /**
+ * Return full border properties as a string
+ *
+ * Border properties are returned just as specified in CSS:
+ * `[width] [style] [color]`
+ * e.g. "1px solid blue"
+ *
+ * @return string
+ *
+ * @link https://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
+ */
+ protected function _get_border_top(): string
+ {
+ return $this->get_border_side("top");
+ }
+
+ /**
+ * @return string
+ */
+ protected function _get_border_right(): string
+ {
+ return $this->get_border_side("right");
+ }
+
+ /**
+ * @return string
+ */
+ protected function _get_border_bottom(): string
+ {
+ return $this->get_border_side("bottom");
+ }
+
+ /**
+ * @return string
+ */
+ protected function _get_border_left(): string
+ {
+ return $this->get_border_side("left");
+ }
+
+ public function has_border_radius(): bool
+ {
+ if (isset($this->has_border_radius_cache)) {
+ return $this->has_border_radius_cache;
+ }
+
+ // Use a fixed ref size here. We don't know the border-box width here
+ // and font size might be 0. Since we are only interested in whether
+ // there is any border radius at all, this should do
+ $tl = (float) $this->length_in_pt($this->border_top_left_radius, 12);
+ $tr = (float) $this->length_in_pt($this->border_top_right_radius, 12);
+ $br = (float) $this->length_in_pt($this->border_bottom_right_radius, 12);
+ $bl = (float) $this->length_in_pt($this->border_bottom_left_radius, 12);
+
+ $this->has_border_radius_cache = $tl + $tr + $br + $bl > 0;
+ return $this->has_border_radius_cache;
+ }
+
+ /**
+ * Get the final border-radius values to use.
+ *
+ * Percentage values are resolved relative to the width of the border box.
+ * The border radius is additionally scaled for the given render box, and
+ * constrained by its width and height.
+ *
+ * @param float[] $border_box The border box of the frame.
+ * @param float[]|null $render_box The box to resolve the border radius for.
+ *
+ * @return float[] A 4-tuple of top-left, top-right, bottom-right, and bottom-left radius.
+ */
+ public function resolve_border_radius(
+ array $border_box,
+ ?array $render_box = null
+ ): array {
+ $render_box = $render_box ?? $border_box;
+ $use_cache = $render_box === $border_box;
+
+ if ($use_cache && isset($this->resolved_border_radius)) {
+ return $this->resolved_border_radius;
+ }
+
+ [$x, $y, $w, $h] = $border_box;
+
+ // Resolve percentages relative to width, as long as we have no support
+ // for per-axis radii
+ $tl = (float) $this->length_in_pt($this->border_top_left_radius, $w);
+ $tr = (float) $this->length_in_pt($this->border_top_right_radius, $w);
+ $br = (float) $this->length_in_pt($this->border_bottom_right_radius, $w);
+ $bl = (float) $this->length_in_pt($this->border_bottom_left_radius, $w);
+
+ if ($tl + $tr + $br + $bl > 0) {
+ [$rx, $ry, $rw, $rh] = $render_box;
+
+ $t_offset = $y - $ry;
+ $r_offset = $rx + $rw - $x - $w;
+ $b_offset = $ry + $rh - $y - $h;
+ $l_offset = $x - $rx;
+
+ if ($tl > 0) {
+ $tl = max($tl + ($t_offset + $l_offset) / 2, 0);
+ }
+ if ($tr > 0) {
+ $tr = max($tr + ($t_offset + $r_offset) / 2, 0);
+ }
+ if ($br > 0) {
+ $br = max($br + ($b_offset + $r_offset) / 2, 0);
+ }
+ if ($bl > 0) {
+ $bl = max($bl + ($b_offset + $l_offset) / 2, 0);
+ }
+
+ if ($tl + $bl > $rh) {
+ $f = $rh / ($tl + $bl);
+ $tl = $f * $tl;
+ $bl = $f * $bl;
+ }
+ if ($tr + $br > $rh) {
+ $f = $rh / ($tr + $br);
+ $tr = $f * $tr;
+ $br = $f * $br;
+ }
+ if ($tl + $tr > $rw) {
+ $f = $rw / ($tl + $tr);
+ $tl = $f * $tl;
+ $tr = $f * $tr;
+ }
+ if ($bl + $br > $rw) {
+ $f = $rw / ($bl + $br);
+ $bl = $f * $bl;
+ $br = $f * $br;
+ }
+ }
+
+ $values = [$tl, $tr, $br, $bl];
+
+ if ($use_cache) {
+ $this->resolved_border_radius = $values;
+ }
+
+ return $values;
+ }
+
+ /**
+ * Returns the outline color as an array
+ *
+ * See {@link Style::_get_color()} for format of the color array.
+ *
+ * @param string $computed
+ * @return array|string
+ *
+ * @link https://www.w3.org/TR/css-ui-4/#propdef-outline-color
+ */
+ protected function _get_outline_color($computed)
+ {
+ return $this->get_color_value($computed);
+ }
+
+ /**
+ * @param string $computed
+ * @return string
+ *
+ * @link https://www.w3.org/TR/css-ui-4/#propdef-outline-style
+ */
+ protected function _get_outline_style($computed): string
+ {
+ return $computed === "auto" ? "solid" : $computed;
+ }
+
+ /**
+ * Return full outline properties as a string
+ *
+ * Outline properties are returned just as specified in CSS:
+ * `[width] [style] [color]`
+ * e.g. "1px solid blue"
+ *
+ * @return string
+ *
+ * @link https://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
+ */
+ protected function _get_outline(): string
+ {
+ $color = $this->__get("outline_color");
+
+ return $this->__get("outline_width") . " " .
+ $this->__get("outline_style") . " " .
+ (\is_array($color) ? $color["hex"] : $color);
+ }
+
+ /**
+ * Returns the list style image URI, or "none"
+ *
+ * @param string $computed
+ * @return string
+ *
+ * @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
+ */
+ protected function _get_list_style_image($computed): string
+ {
+ return $this->_stylesheet->resolve_url($computed);
+ }
+
+ /**
+ * @param string $value
+ * @param int $default
+ *
+ * @return array|string
+ */
+ protected function parse_counter_prop(string $value, int $default)
+ {
+ $ident = self::CSS_IDENTIFIER;
+ $integer = self::CSS_INTEGER;
+ $pattern = "/($ident)(?:\s+($integer))?/";
+
+ if (!preg_match_all($pattern, $value, $matches, PREG_SET_ORDER)) {
+ return "none";
+ }
+
+ $counters = [];
+
+ foreach ($matches as $match) {
+ $counter = $match[1];
+ $value = isset($match[2]) ? (int) $match[2] : $default;
+ $counters[$counter] = $value;
+ }
+
+ return $counters;
+ }
+
+ /**
+ * @param string $computed
+ * @return array|string
+ *
+ * @link https://www.w3.org/TR/CSS21/generate.html#propdef-counter-increment
+ */
+ protected function _get_counter_increment($computed)
+ {
+ if ($computed === "none") {
+ return $computed;
+ }
+
+ return $this->parse_counter_prop($computed, 1);
+ }
+
+ /**
+ * @param string $computed
+ * @return array|string
+ *
+ * @link https://www.w3.org/TR/CSS21/generate.html#propdef-counter-reset
+ */
+ protected function _get_counter_reset($computed)
+ {
+ if ($computed === "none") {
+ return $computed;
+ }
+
+ return $this->parse_counter_prop($computed, 0);
+ }
+
+ /**
+ * @param string $computed
+ * @return string[]|string
+ *
+ * @link https://www.w3.org/TR/CSS21/generate.html#propdef-content
+ */
+ protected function _get_content($computed)
+ {
+ if ($computed === "normal" || $computed === "none") {
+ return $computed;
+ }
+
+ return $this->parse_property_value($computed);
+ }
+
+ /*==============================*/
+
+ /**
+ * Parse a property value into its components.
+ *
+ * @param string $value
+ *
+ * @return string[]
+ */
+ protected function parse_property_value(string $value): array
+ {
+ $ident = self::CSS_IDENTIFIER;
+ $number = self::CSS_NUMBER;
+
+ $pattern = "/\n" .
+ "\s* \" ( (?:[^\"]|\\\\[\"])* ) (?munge_color($val)
+ : $val;
+
+ if ($munged_color === null) {
+ return null;
+ }
+
+ return \is_array($munged_color) ? $munged_color["hex"] : $munged_color;
+ }
+
+ /**
+ * @param string $val
+ * @return int|null
+ */
+ protected function compute_integer(string $val): ?int
+ {
+ $integer = self::CSS_INTEGER;
+ return preg_match("/^$integer$/", $val)
+ ? (int) $val
+ : null;
+ }
+
+ /**
+ * @param string $val
+ * @return float|null
+ */
+ protected function compute_length(string $val): ?float
+ {
+ return mb_strpos($val, "%") === false
+ ? $this->single_length_in_pt($val)
+ : null;
+ }
+
+ /**
+ * @param string $val
+ * @return float|null
+ */
+ protected function compute_length_positive(string $val): ?float
+ {
+ $computed = $this->compute_length($val);
+ return $computed !== null && $computed >= 0 ? $computed : null;
+ }
+
+ /**
+ * @param string $val
+ * @return float|string|null
+ */
+ protected function compute_length_percentage(string $val)
+ {
+ // Compute with a fixed ref size to decide whether percentage values
+ // are valid
+ $computed = $this->single_length_in_pt($val, 12);
+
+ if ($computed === null) {
+ return null;
+ }
+
+ // Retain valid percentage declarations
+ return mb_strpos($val, "%") === false ? $computed : $val;
+ }
+
+ /**
+ * @param string $val
+ * @return float|string|null
+ */
+ protected function compute_length_percentage_positive(string $val)
+ {
+ // Compute with a fixed ref size to decide whether percentage values
+ // are valid
+ $computed = $this->single_length_in_pt($val, 12);
+
+ if ($computed === null || $computed < 0) {
+ return null;
+ }
+
+ // Retain valid percentage declarations
+ return mb_strpos($val, "%") === false ? $computed : $val;
+ }
+
+ /**
+ * @param string $val
+ * @param string $style_prop The corresponding border-/outline-style property.
+ *
+ * @return float|null
+ *
+ * @link https://www.w3.org/TR/css-backgrounds-3/#typedef-line-width
+ */
+ protected function compute_line_width(string $val, string $style_prop): ?float
+ {
+ // Border-width keywords
+ if ($val === "thin") {
+ $computed = 0.5;
+ } elseif ($val === "medium") {
+ $computed = 1.5;
+ } elseif ($val === "thick") {
+ $computed = 2.5;
+ } else {
+ $computed = $this->compute_length_positive($val);
+ }
+
+ if ($computed === null) {
+ return null;
+ }
+
+ // Computed width is 0 if the line style is `none` or `hidden`
+ // https://www.w3.org/TR/css-backgrounds-3/#border-width
+ // https://www.w3.org/TR/css-ui-4/#outline-width
+ $lineStyle = $this->__get($style_prop);
+ $hasLineStyle = $lineStyle !== "none" && $lineStyle !== "hidden";
+
+ return $hasLineStyle ? $computed : 0.0;
+ }
+
+ /**
+ * @param string $val
+ * @return string|null
+ */
+ protected function compute_border_style(string $val): ?string
+ {
+ return \in_array($val, self::BORDER_STYLES, true) ? $val : null;
+ }
+
+ /**
+ * Parse a property value with 1 to 4 components into 4 values, as required
+ * by shorthand properties such as `margin`, `padding`, and `border-radius`.
+ *
+ * @param string $prop The shorthand property with exactly 4 sub-properties to handle.
+ * @param string $value The property value to parse.
+ *
+ * @return string[]
+ */
+ protected function set_quad_shorthand(string $prop, string $value): array
+ {
+ $v = $this->parse_property_value($value);
+
+ switch (\count($v)) {
+ case 1:
+ $values = [$v[0], $v[0], $v[0], $v[0]];
+ break;
+ case 2:
+ $values = [$v[0], $v[1], $v[0], $v[1]];
+ break;
+ case 3:
+ $values = [$v[0], $v[1], $v[2], $v[1]];
+ break;
+ case 4:
+ $values = [$v[0], $v[1], $v[2], $v[3]];
+ break;
+ default:
+ return [];
+ }
+
+ return array_combine(self::$_props_shorthand[$prop], $values);
+ }
+
+ /*======================*/
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/visuren.html#display-prop
+ */
+ protected function _compute_display(string $val)
+ {
+ // Make sure that common valid, but unsupported display types have an
+ // appropriate fallback display type
+ switch ($val) {
+ case "flow-root":
+ case "flex":
+ case "grid":
+ case "table-caption":
+ $val = "block";
+ break;
+ case "inline-flex":
+ case "inline-grid":
+ $val = "inline-block";
+ break;
+ }
+
+ if (!isset(self::$valid_display_types[$val])) {
+ return null;
+ }
+
+ // https://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
+ if ($this->is_in_flow()) {
+ return $val;
+ } else {
+ switch ($val) {
+ case "inline":
+ case "inline-block":
+ // case "table-row-group":
+ // case "table-header-group":
+ // case "table-footer-group":
+ // case "table-row":
+ // case "table-cell":
+ // case "table-column-group":
+ // case "table-column":
+ // case "table-caption":
+ return "block";
+ case "inline-table":
+ return "table";
+ default:
+ return $val;
+ }
+ }
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-color
+ */
+ protected function _compute_color(string $color)
+ {
+ return $this->compute_color_value($color);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-color
+ */
+ protected function _compute_background_color(string $color)
+ {
+ return $this->compute_color_value($color);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
+ */
+ protected function _compute_background_image(string $val)
+ {
+ $parsed_val = $this->_stylesheet->resolve_url($val);
+
+ if ($parsed_val === "none") {
+ return "none";
+ } else {
+ return "url($parsed_val)";
+ }
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
+ */
+ protected function _compute_background_repeat(string $val)
+ {
+ $keywords = ["repeat", "repeat-x", "repeat-y", "no-repeat"];
+ return \in_array($val, $keywords, true) ? $val : null;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
+ */
+ protected function _compute_background_attachment(string $val)
+ {
+ $keywords = ["scroll", "fixed"];
+ return \in_array($val, $keywords, true) ? $val : null;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-position
+ */
+ protected function _compute_background_position(string $val)
+ {
+ $parts = preg_split("/\s+/", $val);
+
+ if (\count($parts) > 2) {
+ return null;
+ }
+
+ switch ($parts[0]) {
+ case "left":
+ $x = "0%";
+ break;
+
+ case "right":
+ $x = "100%";
+ break;
+
+ case "top":
+ $y = "0%";
+ break;
+
+ case "bottom":
+ $y = "100%";
+ break;
+
+ case "center":
+ $x = "50%";
+ $y = "50%";
+ break;
+
+ default:
+ $x = $parts[0];
+ break;
+ }
+
+ if (isset($parts[1])) {
+ switch ($parts[1]) {
+ case "left":
+ $x = "0%";
+ break;
+
+ case "right":
+ $x = "100%";
+ break;
+
+ case "top":
+ $y = "0%";
+ break;
+
+ case "bottom":
+ $y = "100%";
+ break;
+
+ case "center":
+ if ($parts[0] === "left" || $parts[0] === "right" || $parts[0] === "center") {
+ $y = "50%";
+ } else {
+ $x = "50%";
+ }
+ break;
+
+ default:
+ $y = $parts[1];
+ break;
+ }
+ } else {
+ $y = "50%";
+ }
+
+ if (!isset($x)) {
+ $x = "0%";
+ }
+
+ if (!isset($y)) {
+ $y = "0%";
+ }
+
+ return [$x, $y];
+ }
+
+ /**
+ * Compute `background-size`.
+ *
+ * Computes to one of the following values:
+ * * `cover`
+ * * `contain`
+ * * `[width, height]`, each being a length, percentage, or `auto`
+ *
+ * @link https://www.w3.org/TR/css-backgrounds-3/#background-size
+ */
+ protected function _compute_background_size(string $val)
+ {
+ if ($val === "cover" || $val === "contain") {
+ return $val;
+ }
+
+ $parts = preg_split("/\s+/", $val);
+
+ if (\count($parts) > 2) {
+ return null;
+ }
+
+ $width = $parts[0];
+ if ($width !== "auto") {
+ $width = $this->compute_length_percentage_positive($width);
+ }
+
+ $height = $parts[1] ?? "auto";
+ if ($height !== "auto") {
+ $height = $this->compute_length_percentage_positive($height);
+ }
+
+ if ($width === null || $height === null) {
+ return null;
+ }
+
+ return [$width, $height];
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-backgrounds-3/#propdef-background
+ */
+ protected function _set_background(string $value): array
+ {
+ $components = $this->parse_property_value($value);
+ $props = [];
+ $pos_size = [];
+
+ foreach ($components as $val) {
+ if ($val === "none" || mb_substr($val, 0, 4) === "url(") {
+ $props["background_image"] = $val;
+ } elseif ($val === "scroll" || $val === "fixed") {
+ $props["background_attachment"] = $val;
+ } elseif ($val === "repeat" || $val === "repeat-x" || $val === "repeat-y" || $val === "no-repeat") {
+ $props["background_repeat"] = $val;
+ } elseif ($this->is_color_value($val)) {
+ $props["background_color"] = $val;
+ } else {
+ $pos_size[] = $val;
+ }
+ }
+
+ if (\count($pos_size)) {
+ // Split value list at "/"
+ $index = array_search("/", $pos_size, true);
+
+ if ($index !== false) {
+ $pos = \array_slice($pos_size, 0, $index);
+ $size = \array_slice($pos_size, $index + 1);
+ } else {
+ $pos = $pos_size;
+ $size = [];
+ }
+
+ $props["background_position"] = implode(" ", $pos);
+
+ if (\count($size)) {
+ $props["background_size"] = implode(" ", $size);
+ }
+ }
+
+ return $props;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
+ */
+ protected function _compute_font_size(string $size)
+ {
+ $parent_font_size = isset($this->parent_style)
+ ? $this->parent_style->__get("font_size")
+ : self::$default_font_size;
+
+ switch ($size) {
+ case "xx-small":
+ case "x-small":
+ case "small":
+ case "medium":
+ case "large":
+ case "x-large":
+ case "xx-large":
+ $fs = self::$default_font_size * self::$font_size_keywords[$size];
+ break;
+
+ case "smaller":
+ $fs = 8 / 9 * $parent_font_size;
+ break;
+
+ case "larger":
+ $fs = 6 / 5 * $parent_font_size;
+ break;
+
+ default:
+ $fs = $this->single_length_in_pt($size, $parent_font_size, $parent_font_size);
+ break;
+ }
+
+ return $fs;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/fonts.html#font-boldness
+ */
+ protected function _compute_font_weight(string $weight)
+ {
+ $computed_weight = $weight;
+
+ if ($weight === "bolder") {
+ //TODO: One font weight heavier than the parent element (among the available weights of the font).
+ $computed_weight = "bold";
+ } elseif ($weight === "lighter") {
+ //TODO: One font weight lighter than the parent element (among the available weights of the font).
+ $computed_weight = "normal";
+ }
+
+ return $computed_weight;
+ }
+
+ /**
+ * Handle the `font` shorthand property.
+ *
+ * `[ font-style || font-variant || font-weight ] font-size [ / line-height ] font-family`
+ *
+ * @link https://www.w3.org/TR/CSS21/fonts.html#font-shorthand
+ */
+ protected function _set_font(string $value): array
+ {
+ $components = $this->parse_property_value($value);
+ $props = [];
+
+ $number = self::CSS_NUMBER;
+ $unit = "pt|px|pc|rem|em|ex|in|cm|mm|%";
+ $sizePattern = "/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|$number(?:$unit))$/";
+ $sizeIndex = null;
+
+ // Find index of font-size to split the component list
+ foreach ($components as $i => $val) {
+ if (preg_match($sizePattern, $val)) {
+ $sizeIndex = $i;
+ $props["font_size"] = $val;
+ break;
+ }
+ }
+
+ // `font-size` is mandatory
+ if ($sizeIndex === null) {
+ return [];
+ }
+
+ // `font-style`, `font-variant`, `font-weight` in any order
+ $styleVariantWeight = \array_slice($components, 0, $sizeIndex);
+ $stylePattern = "/^(italic|oblique)$/";
+ $variantPattern = "/^(small-caps)$/";
+ $weightPattern = "/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900)$/";
+
+ if (\count($styleVariantWeight) > 3) {
+ return [];
+ }
+
+ foreach ($styleVariantWeight as $val) {
+ if ($val === "normal") {
+ // Ignore any `normal` value, as it is valid and the initial
+ // value for all three properties
+ } elseif (!isset($props["font_style"]) && preg_match($stylePattern, $val)) {
+ $props["font_style"] = $val;
+ } elseif (!isset($props["font_variant"]) && preg_match($variantPattern, $val)) {
+ $props["font_variant"] = $val;
+ } elseif (!isset($props["font_weight"]) && preg_match($weightPattern, $val)) {
+ $props["font_weight"] = $val;
+ } else {
+ // Duplicates and other values disallowed here
+ return [];
+ }
+ }
+
+ // Optional slash + `line-height` followed by mandatory `font-family`
+ $lineFamily = \array_slice($components, $sizeIndex + 1);
+ $hasLineHeight = $lineFamily !== [] && $lineFamily[0] === "/";
+ $lineHeight = $hasLineHeight ? \array_slice($lineFamily, 1, 1) : [];
+ $fontFamily = $hasLineHeight ? \array_slice($lineFamily, 2) : $lineFamily;
+ $lineHeightPattern = "/^(normal|$number(?:$unit)?)$/";
+
+ // Missing `font-family` or `line-height` after slash
+ if ($fontFamily === []
+ || ($hasLineHeight && !preg_match($lineHeightPattern, $lineHeight[0]))
+ ) {
+ return [];
+ }
+
+ if ($hasLineHeight) {
+ $props["line_height"] = $lineHeight[0];
+ }
+
+ $props["font_family"] = implode("", $fontFamily);
+
+ return $props;
+ }
+
+ /**
+ * Compute `text-align`.
+ *
+ * If no alignment is set on the element and the direction is rtl then
+ * the property is set to "right", otherwise it is set to "left".
+ *
+ * @link https://www.w3.org/TR/CSS21/text.html#propdef-text-align
+ */
+ protected function _compute_text_align(string $val)
+ {
+ $alignment = $val;
+ if ($alignment === "") {
+ $alignment = "left";
+ if ($this->__get("direction") === "rtl") {
+ $alignment = "right";
+ }
+ }
+
+ if (!\in_array($alignment, self::TEXT_ALIGN_KEYWORDS, true)) {
+ return null;
+ }
+
+ return $alignment;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-text-4/#word-spacing-property
+ */
+ protected function _compute_word_spacing(string $val)
+ {
+ if ($val === "normal") {
+ return 0.0;
+ }
+
+ return $this->compute_length_percentage($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-text-4/#letter-spacing-property
+ */
+ protected function _compute_letter_spacing(string $val)
+ {
+ if ($val === "normal") {
+ return 0.0;
+ }
+
+ return $this->compute_length_percentage($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
+ */
+ protected function _compute_line_height(string $val)
+ {
+ if ($val === "normal") {
+ return $val;
+ }
+
+ // Compute number values to string and lengths to float (in pt)
+ if (is_numeric($val)) {
+ return (string) $val;
+ }
+
+ $font_size = $this->__get("font_size");
+ $computed = $this->single_length_in_pt($val, $font_size);
+ return $computed !== null && $computed >= 0 ? $computed : null;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-text-3/#text-indent-property
+ */
+ protected function _compute_text_indent(string $val)
+ {
+ return $this->compute_length_percentage($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/page.html#propdef-page-break-before
+ */
+ protected function _compute_page_break_before(string $break)
+ {
+ if ($break === "left" || $break === "right") {
+ $break = "always";
+ }
+
+ return $break;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/page.html#propdef-page-break-after
+ */
+ protected function _compute_page_break_after(string $break)
+ {
+ if ($break === "left" || $break === "right") {
+ $break = "always";
+ }
+
+ return $break;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-width
+ */
+ protected function _compute_width(string $val)
+ {
+ if ($val === "auto") {
+ return $val;
+ }
+
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-height
+ */
+ protected function _compute_height(string $val)
+ {
+ if ($val === "auto") {
+ return $val;
+ }
+
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-min-width
+ */
+ protected function _compute_min_width(string $val)
+ {
+ // Legacy support for `none`, not covered by spec
+ if ($val === "auto" || $val === "none") {
+ return "auto";
+ }
+
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-min-height
+ */
+ protected function _compute_min_height(string $val)
+ {
+ // Legacy support for `none`, not covered by spec
+ if ($val === "auto" || $val === "none") {
+ return "auto";
+ }
+
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-max-width
+ */
+ protected function _compute_max_width(string $val)
+ {
+ // Legacy support for `auto`, not covered by spec
+ if ($val === "none" || $val === "auto") {
+ return "none";
+ }
+
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-max-height
+ */
+ protected function _compute_max_height(string $val)
+ {
+ // Legacy support for `auto`, not covered by spec
+ if ($val === "none" || $val === "auto") {
+ return "none";
+ }
+
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-position-3/#inset-properties
+ * @link https://www.w3.org/TR/css-position-3/#propdef-inset
+ */
+ protected function _set_inset(string $val): array
+ {
+ return $this->set_quad_shorthand("inset", $val);
+ }
+
+ /**
+ * @param string $val
+ * @return float|string|null
+ */
+ protected function compute_box_inset(string $val)
+ {
+ if ($val === "auto") {
+ return $val;
+ }
+
+ return $this->compute_length_percentage($val);
+ }
+
+ protected function _compute_top(string $val)
+ {
+ return $this->compute_box_inset($val);
+ }
+
+ protected function _compute_right(string $val)
+ {
+ return $this->compute_box_inset($val);
+ }
+
+ protected function _compute_bottom(string $val)
+ {
+ return $this->compute_box_inset($val);
+ }
+
+ protected function _compute_left(string $val)
+ {
+ return $this->compute_box_inset($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/box.html#margin-properties
+ * @link https://www.w3.org/TR/CSS21/box.html#propdef-margin
+ */
+ protected function _set_margin(string $val): array
+ {
+ return $this->set_quad_shorthand("margin", $val);
+ }
+
+ /**
+ * @param string $val
+ * @return float|string|null
+ */
+ protected function compute_margin(string $val)
+ {
+ // Legacy support for `none` keyword, not covered by spec
+ if ($val === "none") {
+ return 0.0;
+ }
+
+ if ($val === "auto") {
+ return $val;
+ }
+
+ return $this->compute_length_percentage($val);
+ }
+
+ protected function _compute_margin_top(string $val)
+ {
+ return $this->compute_margin($val);
+ }
+
+ protected function _compute_margin_right(string $val)
+ {
+ return $this->compute_margin($val);
+ }
+
+ protected function _compute_margin_bottom(string $val)
+ {
+ return $this->compute_margin($val);
+ }
+
+ protected function _compute_margin_left(string $val)
+ {
+ return $this->compute_margin($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/box.html#padding-properties
+ * @link https://www.w3.org/TR/CSS21/box.html#propdef-padding
+ */
+ protected function _set_padding(string $val): array
+ {
+ return $this->set_quad_shorthand("padding", $val);
+ }
+
+ /**
+ * @param string $val
+ * @return float|string|null
+ */
+ protected function compute_padding(string $val)
+ {
+ // Legacy support for `none` keyword, not covered by spec
+ if ($val === "none") {
+ return 0.0;
+ }
+
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ protected function _compute_padding_top(string $val)
+ {
+ return $this->compute_padding($val);
+ }
+
+ protected function _compute_padding_right(string $val)
+ {
+ return $this->compute_padding($val);
+ }
+
+ protected function _compute_padding_bottom(string $val)
+ {
+ return $this->compute_padding($val);
+ }
+
+ protected function _compute_padding_left(string $val)
+ {
+ return $this->compute_padding($val);
+ }
+
+ /**
+ * @param string $value `width || style || color`
+ * @param string[] $styles The list of border styles to accept.
+ *
+ * @return array Array of `[width, style, color]`, or `null` if the declaration is invalid.
+ */
+ protected function parse_border_side(string $value, array $styles = self::BORDER_STYLES): ?array
+ {
+ $components = $this->parse_property_value($value);
+ $width = null;
+ $style = null;
+ $color = null;
+
+ foreach ($components as $val) {
+ if ($style === null && \in_array($val, $styles, true)) {
+ $style = $val;
+ } elseif ($color === null && $this->is_color_value($val)) {
+ $color = $val;
+ } elseif ($width === null) {
+ // Assume width
+ $width = $val;
+ } else {
+ // Duplicates are not allowed
+ return null;
+ }
+ }
+
+ return [$width, $style, $color];
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/box.html#border-properties
+ * @link https://www.w3.org/TR/CSS21/box.html#propdef-border
+ */
+ protected function _set_border(string $value): array
+ {
+ $values = $this->parse_border_side($value);
+
+ if ($values === null) {
+ return [];
+ }
+
+ return array_merge(
+ array_combine(self::$_props_shorthand["border_top"], $values),
+ array_combine(self::$_props_shorthand["border_right"], $values),
+ array_combine(self::$_props_shorthand["border_bottom"], $values),
+ array_combine(self::$_props_shorthand["border_left"], $values)
+ );
+ }
+
+ /**
+ * @param string $prop
+ * @param string $value
+ * @return array
+ */
+ protected function set_border_side(string $prop, string $value): array
+ {
+ $values = $this->parse_border_side($value);
+
+ if ($values === null) {
+ return [];
+ }
+
+ return array_combine(self::$_props_shorthand[$prop], $values);
+ }
+
+ protected function _set_border_top(string $val): array
+ {
+ return $this->set_border_side("border_top", $val);
+ }
+
+ protected function _set_border_right(string $val): array
+ {
+ return $this->set_border_side("border_right", $val);
+ }
+
+ protected function _set_border_bottom(string $val): array
+ {
+ return $this->set_border_side("border_bottom", $val);
+ }
+
+ protected function _set_border_left(string $val): array
+ {
+ return $this->set_border_side("border_left", $val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/box.html#propdef-border-color
+ */
+ protected function _set_border_color(string $val): array
+ {
+ return $this->set_quad_shorthand("border_color", $val);
+ }
+
+ protected function _compute_border_top_color(string $val)
+ {
+ return $this->compute_color_value($val);
+ }
+
+ protected function _compute_border_right_color(string $val)
+ {
+ return $this->compute_color_value($val);
+ }
+
+ protected function _compute_border_bottom_color(string $val)
+ {
+ return $this->compute_color_value($val);
+ }
+
+ protected function _compute_border_left_color(string $val)
+ {
+ return $this->compute_color_value($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/box.html#propdef-border-style
+ */
+ protected function _set_border_style(string $val): array
+ {
+ return $this->set_quad_shorthand("border_style", $val);
+ }
+
+ protected function _compute_border_top_style(string $val)
+ {
+ return $this->compute_border_style($val);
+ }
+
+ protected function _compute_border_right_style(string $val)
+ {
+ return $this->compute_border_style($val);
+ }
+
+ protected function _compute_border_bottom_style(string $val)
+ {
+ return $this->compute_border_style($val);
+ }
+
+ protected function _compute_border_left_style(string $val)
+ {
+ return $this->compute_border_style($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/box.html#propdef-border-width
+ */
+ protected function _set_border_width(string $val): array
+ {
+ return $this->set_quad_shorthand("border_width", $val);
+ }
+
+ protected function _compute_border_top_width(string $val)
+ {
+ return $this->compute_line_width($val, "border_top_style");
+ }
+
+ protected function _compute_border_right_width(string $val)
+ {
+ return $this->compute_line_width($val, "border_right_style");
+ }
+
+ protected function _compute_border_bottom_width(string $val)
+ {
+ return $this->compute_line_width($val, "border_bottom_style");
+ }
+
+ protected function _compute_border_left_width(string $val)
+ {
+ return $this->compute_line_width($val, "border_left_style");
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-backgrounds-3/#corners
+ * @link https://www.w3.org/TR/css-backgrounds-3/#propdef-border-radius
+ */
+ protected function _set_border_radius(string $val): array
+ {
+ return $this->set_quad_shorthand("border_radius", $val);
+ }
+
+ protected function _compute_border_top_left_radius(string $val)
+ {
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ protected function _compute_border_top_right_radius(string $val)
+ {
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ protected function _compute_border_bottom_right_radius(string $val)
+ {
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ protected function _compute_border_bottom_left_radius(string $val)
+ {
+ return $this->compute_length_percentage_positive($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-ui-4/#outline-props
+ * @link https://www.w3.org/TR/css-ui-4/#propdef-outline
+ */
+ protected function _set_outline(string $value): array
+ {
+ $values = $this->parse_border_side($value, self::OUTLINE_STYLES);
+
+ if ($values === null) {
+ return [];
+ }
+
+ return array_combine(self::$_props_shorthand["outline"], $values);
+ }
+
+ protected function _compute_outline_color(string $val)
+ {
+ return $this->compute_color_value($val);
+ }
+
+ protected function _compute_outline_style(string $val)
+ {
+ return \in_array($val, self::OUTLINE_STYLES, true) ? $val : null;
+ }
+
+ protected function _compute_outline_width(string $val)
+ {
+ return $this->compute_line_width($val, "outline_style");
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-ui-4/#propdef-outline-offset
+ */
+ protected function _compute_outline_offset(string $val)
+ {
+ return $this->compute_length($val);
+ }
+
+ /**
+ * Compute `border-spacing` to two lengths of the form
+ * `[horizontal, vertical]`.
+ *
+ * @link https://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
+ */
+ protected function _compute_border_spacing(string $val)
+ {
+ $parts = preg_split("/\s+/", $val);
+
+ if (\count($parts) > 2) {
+ return null;
+ }
+
+ $h = $this->compute_length_positive($parts[0]);
+ $v = isset($parts[1])
+ ? $this->compute_length_positive($parts[1])
+ : $h;
+
+ if ($h === null || $v === null) {
+ return null;
+ }
+
+ return [$h, $v];
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
+ */
+ protected function _compute_list_style_image(string $val)
+ {
+ $parsed_val = $this->_stylesheet->resolve_url($val);
+
+ if ($parsed_val === "none") {
+ return "none";
+ } else {
+ return "url($parsed_val)";
+ }
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style
+ */
+ protected function _set_list_style(string $value): array
+ {
+ static $positions = ["inside", "outside"];
+ static $types = [
+ "disc", "circle", "square",
+ "decimal-leading-zero", "decimal", "1",
+ "lower-roman", "upper-roman", "a", "A",
+ "lower-greek",
+ "lower-latin", "upper-latin",
+ "lower-alpha", "upper-alpha",
+ "armenian", "georgian", "hebrew",
+ "cjk-ideographic", "hiragana", "katakana",
+ "hiragana-iroha", "katakana-iroha", "none"
+ ];
+
+ $components = $this->parse_property_value($value);
+ $props = [];
+
+ foreach ($components as $val) {
+ /* https://www.w3.org/TR/CSS21/generate.html#list-style
+ * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
+ */
+ if ($val === "none") {
+ $props["list_style_type"] = $val;
+ $props["list_style_image"] = $val;
+ continue;
+ }
+
+ //On setting or merging or inheriting list_style_image as well as list_style_type,
+ //and url exists, then url has precedence, otherwise fall back to list_style_type
+ //Firefox is wrong here (list_style_image gets overwritten on explicit list_style_type)
+ //Internet Explorer 7/8 and dompdf is right.
+
+ if (mb_substr($val, 0, 4) === "url(") {
+ $props["list_style_image"] = $val;
+ continue;
+ }
+
+ if (\in_array($val, $types, true)) {
+ $props["list_style_type"] = $val;
+ } elseif (\in_array($val, $positions, true)) {
+ $props["list_style_position"] = $val;
+ }
+ }
+
+ return $props;
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-page-3/#page-size-prop
+ */
+ protected function _compute_size(string $val)
+ {
+ if ($val === "auto") {
+ return $val;
+ }
+
+ $parts = $this->parse_property_value($val);
+ $count = \count($parts);
+
+ if ($count === 0 || $count > 3) {
+ return null;
+ }
+
+ $size = null;
+ $orientation = null;
+ $lengths = [];
+
+ foreach ($parts as $part) {
+ if ($size === null && isset(CPDF::$PAPER_SIZES[$part])) {
+ $size = $part;
+ } elseif ($orientation === null && ($part === "portrait" || $part === "landscape")) {
+ $orientation = $part;
+ } else {
+ $lengths[] = $part;
+ }
+ }
+
+ if ($size !== null && $lengths !== []) {
+ return null;
+ }
+
+ if ($size !== null) {
+ // Standard paper size
+ [$l1, $l2] = \array_slice(CPDF::$PAPER_SIZES[$size], 2, 2);
+ } elseif ($lengths === []) {
+ // Orientation only, use default paper size
+ $dims = $this->_stylesheet->get_dompdf()->getPaperSize();
+ [$l1, $l2] = \array_slice($dims, 2, 2);
+ } else {
+ // Custom paper size
+ $l1 = $this->compute_length_positive($lengths[0]);
+ $l2 = isset($lengths[1]) ? $this->compute_length_positive($lengths[1]) : $l1;
+
+ if ($l1 === null || $l2 === null) {
+ return null;
+ }
+ }
+
+ if (($orientation === "portrait" && $l1 > $l2)
+ || ($orientation === "landscape" && $l2 > $l1)
+ ) {
+ return [$l2, $l1];
+ }
+
+ return [$l1, $l2];
+ }
+
+ /**
+ * @param string $computed
+ * @return array
+ *
+ * @link https://www.w3.org/TR/css-transforms-1/#transform-property
+ */
+ protected function _get_transform($computed)
+ {
+ //TODO: should be handled in setter (lengths set to absolute)
+
+ $number = "\s*([^,\s]+)\s*";
+ $tr_value = "\s*([^,\s]+)\s*";
+ $angle = "\s*([^,\s]+(?:deg|rad)?)\s*";
+
+ if (!preg_match_all("/[a-z]+\([^\)]+\)/i", $computed, $parts, PREG_SET_ORDER)) {
+ return [];
+ }
+
+ $functions = [
+ //"matrix" => "\($number,$number,$number,$number,$number,$number\)",
+
+ "translate" => "\($tr_value(?:,$tr_value)?\)",
+ "translateX" => "\($tr_value\)",
+ "translateY" => "\($tr_value\)",
+
+ "scale" => "\($number(?:,$number)?\)",
+ "scaleX" => "\($number\)",
+ "scaleY" => "\($number\)",
+
+ "rotate" => "\($angle\)",
+
+ "skew" => "\($angle(?:,$angle)?\)",
+ "skewX" => "\($angle\)",
+ "skewY" => "\($angle\)",
+ ];
+
+ $transforms = [];
+
+ foreach ($parts as $part) {
+ $t = $part[0];
+
+ foreach ($functions as $name => $pattern) {
+ if (preg_match("/$name\s*$pattern/i", $t, $matches)) {
+ $values = \array_slice($matches, 1);
+
+ switch ($name) {
+ // units
+ case "rotate":
+ case "skew":
+ case "skewX":
+ case "skewY":
+
+ foreach ($values as $i => $value) {
+ if (strpos($value, "rad")) {
+ $values[$i] = rad2deg((float) $value);
+ } else {
+ $values[$i] = (float) $value;
+ }
+ }
+
+ switch ($name) {
+ case "skew":
+ if (!isset($values[1])) {
+ $values[1] = 0;
+ }
+ break;
+ case "skewX":
+ $name = "skew";
+ $values = [$values[0], 0];
+ break;
+ case "skewY":
+ $name = "skew";
+ $values = [0, $values[0]];
+ break;
+ }
+ break;
+
+ // units
+ case "translate":
+ $values[0] = $this->length_in_pt($values[0], (float)$this->length_in_pt($this->width));
+
+ if (isset($values[1])) {
+ $values[1] = $this->length_in_pt($values[1], (float)$this->length_in_pt($this->height));
+ } else {
+ $values[1] = 0;
+ }
+ break;
+
+ case "translateX":
+ $name = "translate";
+ $values = [$this->length_in_pt($values[0], (float)$this->length_in_pt($this->width)), 0];
+ break;
+
+ case "translateY":
+ $name = "translate";
+ $values = [0, $this->length_in_pt($values[0], (float)$this->length_in_pt($this->height))];
+ break;
+
+ // units
+ case "scale":
+ if (!isset($values[1])) {
+ $values[1] = $values[0];
+ }
+ break;
+
+ case "scaleX":
+ $name = "scale";
+ $values = [$values[0], 1.0];
+ break;
+
+ case "scaleY":
+ $name = "scale";
+ $values = [1.0, $values[0]];
+ break;
+ }
+
+ $transforms[] = [
+ $name,
+ $values,
+ ];
+ }
+ }
+ }
+
+ return $transforms;
+ }
+
+ /**
+ * @param string $computed
+ * @return array
+ *
+ * @link https://www.w3.org/TR/css-transforms-1/#transform-origin-property
+ */
+ protected function _get_transform_origin($computed)
+ {
+ //TODO: should be handled in setter
+
+ $values = preg_split("/\s+/", $computed);
+
+ $values = array_map(function ($value) {
+ if (\in_array($value, ["top", "left"], true)) {
+ return 0;
+ } elseif (\in_array($value, ["bottom", "right"], true)) {
+ return "100%";
+ } else {
+ return $value;
+ }
+ }, $values);
+
+ if (!isset($values[1])) {
+ $values[1] = $values[0];
+ }
+
+ return $values;
+ }
+
+ /**
+ * @param string $val
+ * @return string|null
+ */
+ protected function parse_image_resolution(string $val): ?string
+ {
+ // If exif data could be get:
+ // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';
+
+ $re = '/^\s*(\d+|normal|auto)\s*$/';
+
+ if (!preg_match($re, $val, $matches)) {
+ return null;
+ }
+
+ return $matches[1];
+ }
+
+ /**
+ * auto | normal | dpi
+ */
+ protected function _compute_background_image_resolution(string $val)
+ {
+ return $this->parse_image_resolution($val);
+ }
+
+ /**
+ * auto | normal | dpi
+ */
+ protected function _compute_image_resolution(string $val)
+ {
+ return $this->parse_image_resolution($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-break-3/#propdef-orphans
+ */
+ protected function _compute_orphans(string $val)
+ {
+ return $this->compute_integer($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-break-3/#propdef-widows
+ */
+ protected function _compute_widows(string $val)
+ {
+ return $this->compute_integer($val);
+ }
+
+ /**
+ * @link https://www.w3.org/TR/css-color-4/#propdef-opacity
+ */
+ protected function _compute_opacity(string $val)
+ {
+ $number = self::CSS_NUMBER;
+ $pattern = "/^($number)(%?)$/";
+
+ if (!preg_match($pattern, $val, $matches)) {
+ return null;
+ }
+
+ $v = (float) $matches[1];
+ $percent = $matches[2] === "%";
+ $opacity = $percent ? ($v / 100) : $v;
+
+ return max(0.0, min($opacity, 1.0));
+ }
+
+ /**
+ * @link https://www.w3.org/TR/CSS21//visuren.html#propdef-z-index
+ */
+ protected function _compute_z_index(string $val)
+ {
+ if ($val === "auto") {
+ return $val;
+ }
+
+ return $this->compute_integer($val);
+ }
+
+ /**
+ * @param FontMetrics $fontMetrics
+ * @return $this
+ */
+ public function setFontMetrics(FontMetrics $fontMetrics)
+ {
+ $this->fontMetrics = $fontMetrics;
+ return $this;
+ }
+
+ /**
+ * @return FontMetrics
+ */
+ public function getFontMetrics()
+ {
+ return $this->fontMetrics;
+ }
+
+ /**
+ * Generate a string representation of the Style
+ *
+ * This dumps the entire property array into a string via print_r. Useful
+ * for debugging.
+ *
+ * @return string
+ */
+ /*DEBUGCSS print: see below additional debugging util*/
+ public function __toString(): string
+ {
+ $parent_font_size = $this->parent_style
+ ? $this->parent_style->font_size
+ : self::$default_font_size;
+
+ return print_r(array_merge(["parent_font_size" => $parent_font_size],
+ $this->_props), true);
+ }
+
+ /*DEBUGCSS*/
+ public function debug_print(): void
+ {
+ $parent_font_size = $this->parent_style
+ ? $this->parent_style->font_size
+ : self::$default_font_size;
+
+ print " parent_font_size:" . $parent_font_size . ";\n";
+ print " Props [\n";
+ print " specified [\n";
+ foreach ($this->_props as $prop => $val) {
+ print ' ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true));
+ if (isset($this->_important_props[$prop])) {
+ print ' !important';
+ }
+ print ";\n";
+ }
+ print " ]\n";
+ print " computed [\n";
+ foreach ($this->_props_computed as $prop => $val) {
+ print ' ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true));
+ print ";\n";
+ }
+ print " ]\n";
+ print " cached [\n";
+ foreach ($this->_props_used as $prop => $val) {
+ print ' ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true));
+ print ";\n";
+ }
+ print " ]\n";
+ print " ]\n";
+ }
+}
diff --git a/library/vendor/dompdf/src/Css/Stylesheet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Stylesheet.php
similarity index 83%
rename from library/vendor/dompdf/src/Css/Stylesheet.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Stylesheet.php
index 9d1a1ecee..20e019ab0 100644
--- a/library/vendor/dompdf/src/Css/Stylesheet.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Stylesheet.php
@@ -1,10 +1,7 @@
- * @author Helmut Tischer
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\Css;
@@ -66,13 +63,13 @@ class Stylesheet
* not support user stylesheets, and user agent stylesheets can not include
* important declarations.
*/
- private static $_stylesheet_origins = array(
+ private static $_stylesheet_origins = [
self::ORIG_UA => 0x00000000, // user agent declarations
self::ORIG_USER => 0x10000000, // user normal declarations
self::ORIG_AUTHOR => 0x30000000, // author normal declarations
- );
+ ];
- /*
+ /**
* Non-CSS presentational hints (i.e. HTML 4 attributes) are handled as if added
* to the beginning of an author stylesheet, i.e. anything in author stylesheets
* should override them.
@@ -99,7 +96,7 @@ class Stylesheet
*
* @var string
*/
- private $_protocol;
+ private $_protocol = "";
/**
* Base hostname of the document being parsed
@@ -107,7 +104,7 @@ class Stylesheet
*
* @var string
*/
- private $_base_host;
+ private $_base_host = "";
/**
* Base path of the document being parsed
@@ -115,7 +112,7 @@ class Stylesheet
*
* @var string
*/
- private $_base_path;
+ private $_base_path = "";
/**
* The styles defined by @page rules
@@ -152,8 +149,8 @@ class Stylesheet
* (Previous version $ACCEPTED_MEDIA_TYPES = $ACCEPTED_GENERIC_MEDIA_TYPES + $ACCEPTED_DEFAULT_MEDIA_TYPE)
*/
static $ACCEPTED_DEFAULT_MEDIA_TYPE = "print";
- static $ACCEPTED_GENERIC_MEDIA_TYPES = array("all", "static", "visual", "bitmap", "paged", "dompdf");
- static $VALID_MEDIA_TYPES = array("all", "aural", "bitmap", "braille", "dompdf", "embossed", "handheld", "paged", "print", "projection", "screen", "speech", "static", "tty", "tv", "visual");
+ static $ACCEPTED_GENERIC_MEDIA_TYPES = ["all", "static", "visual", "bitmap", "paged", "dompdf"];
+ static $VALID_MEDIA_TYPES = ["all", "aural", "bitmap", "braille", "dompdf", "embossed", "handheld", "paged", "print", "projection", "screen", "speech", "static", "tty", "tv", "visual"];
/**
* @var FontMetrics
@@ -170,14 +167,14 @@ class Stylesheet
{
$this->_dompdf = $dompdf;
$this->setFontMetrics($dompdf->getFontMetrics());
- $this->_styles = array();
- $this->_loaded_files = array();
+ $this->_styles = [];
+ $this->_loaded_files = [];
$script = __FILE__;
- if(isset($_SERVER["SCRIPT_FILENAME"])){
+ if (isset($_SERVER["SCRIPT_FILENAME"])) {
$script = $_SERVER["SCRIPT_FILENAME"];
}
list($this->_protocol, $this->_base_host, $this->_base_path) = Helpers::explode_url($script);
- $this->_page_styles = array("base" => new Style($this));
+ $this->_page_styles = ["base" => new Style($this)];
}
/**
@@ -185,7 +182,7 @@ class Stylesheet
*
* @param string $protocol
*/
- function set_protocol($protocol)
+ function set_protocol(string $protocol)
{
$this->_protocol = $protocol;
}
@@ -195,7 +192,7 @@ class Stylesheet
*
* @param string $host
*/
- function set_host($host)
+ function set_host(string $host)
{
$this->_base_host = $host;
}
@@ -205,7 +202,7 @@ class Stylesheet
*
* @param string $path
*/
- function set_base_path($path)
+ function set_base_path(string $path)
{
$this->_base_path = $path;
}
@@ -260,59 +257,32 @@ class Stylesheet
return $this->_page_styles;
}
+ /**
+ * Create a new Style object associated with this stylesheet
+ *
+ * @return Style
+ */
+ function create_style(): Style
+ {
+ return new Style($this, $this->_current_origin);
+ }
+
/**
* Add a new Style object to the stylesheet
- * add_style() adds a new Style object to the current stylesheet, or
- * merges a new Style with an existing one.
+ *
+ * The style's origin is changed to the current origin of the stylesheet.
*
* @param string $key the Style's selector
* @param Style $style the Style to be added
- *
- * @throws \Dompdf\Exception
*/
- function add_style($key, Style $style)
- {
- if (!is_string($key)) {
- throw new Exception("CSS rule must be keyed by a string.");
- }
-
- if (!isset($this->_styles[$key])) {
- $this->_styles[$key] = array();
- }
- $new_style = clone $style;
- $new_style->set_origin($this->_current_origin);
- $this->_styles[$key][] = $new_style;
- }
-
- /**
- * lookup a specific Style collection
- *
- * lookup() returns the Style collection specified by $key, or null if the Style is
- * not found.
- *
- * @param string $key the selector of the requested Style
- * @return Style
- *
- * @Fixme _styles is a two dimensional array. It should produce wrong results
- */
- function lookup($key)
+ function add_style(string $key, Style $style): void
{
if (!isset($this->_styles[$key])) {
- return null;
+ $this->_styles[$key] = [];
}
- return $this->_styles[$key];
- }
-
- /**
- * create a new Style object associated with this stylesheet
- *
- * @param Style $parent The style of this style's parent in the DOM tree
- * @return Style
- */
- function create_style(Style $parent = null)
- {
- return new Style($this, $this->_current_origin);
+ $style->set_origin($this->_current_origin);
+ $this->_styles[$key][] = $style;
}
/**
@@ -329,7 +299,6 @@ class Stylesheet
$this->_parse_css($css);
}
-
/**
* load and parse a CSS file
*
@@ -353,18 +322,27 @@ class Stylesheet
$parsed = Helpers::parse_data_uri($file);
$css = $parsed["data"];
} else {
+ $options = $this->_dompdf->getOptions();
+
$parsed_url = Helpers::explode_url($file);
+ $protocol = $parsed_url["protocol"];
- list($this->_protocol, $this->_base_host, $this->_base_path, $filename) = $parsed_url;
-
- // Fix submitted by Nick Oostveen for aliased directory support:
- if ($this->_protocol == "") {
- $file = $this->_base_path . $filename;
- } else {
- $file = Helpers::build_url($this->_protocol, $this->_base_host, $this->_base_path, $filename);
+ if ($file !== $this->getDefaultStylesheet()) {
+ $allowed_protocols = $options->getAllowedProtocols();
+ if (!array_key_exists($protocol, $allowed_protocols)) {
+ Helpers::record_warnings(E_USER_WARNING, "Permission denied on $file. The communication protocol is not supported.", __FILE__, __LINE__);
+ return;
+ }
+ foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
+ [$result, $message] = $rule($file);
+ if (!$result) {
+ Helpers::record_warnings(E_USER_WARNING, "Error loading $file: $message", __FILE__, __LINE__);
+ return;
+ }
+ }
}
- list($css, $http_response_header) = Helpers::getFileContent($file, $this->_dompdf->getHttpContext());
+ [$css, $http_response_header] = Helpers::getFileContent($file, $this->_dompdf->getHttpContext());
$good_mime_type = true;
@@ -378,11 +356,12 @@ class Stylesheet
}
}
}
-
- if (!$good_mime_type || $css == "") {
+ if (!$good_mime_type || $css === null) {
Helpers::record_warnings(E_USER_WARNING, "Unable to load css file $file", __FILE__, __LINE__);
return;
}
+
+ [$this->_protocol, $this->_base_host, $this->_base_path] = $parsed_url;
}
$this->_parse_css($css);
@@ -414,7 +393,9 @@ class Stylesheet
$d = min(mb_substr_count($selector, " ") +
mb_substr_count($selector, ">") +
- mb_substr_count($selector, "+"), 255);
+ mb_substr_count($selector, "+") +
+ mb_substr_count($selector, "~") -
+ mb_substr_count($selector, "~="), 255);
//If a normal element name is at the beginning of the string,
//a leading whitespace might have been removed on whitespace collapsing and removal
@@ -422,7 +403,7 @@ class Stylesheet
//this can lead to a too small specificity
//see _css_selector_to_xpath
- if (!in_array($selector[0], array(" ", ">", ".", "#", "+", ":", "[")) && $selector !== "*") {
+ if (!in_array($selector[0], [" ", ">", ".", "#", "+", "~", ":", "["]) && $selector !== "*") {
$d++;
}
@@ -447,9 +428,8 @@ class Stylesheet
* @throws Exception
* @return array
*/
- private function _css_selector_to_xpath($selector, $first_pass = false)
+ private function _css_selector_to_xpath(string $selector, bool $first_pass = false): array
{
-
// Collapse white space and strip whitespace around delimiters
//$search = array("/\\s+/", "/\\s+([.>#+:])\\s+/");
//$replace = array(" ", "\\1");
@@ -459,15 +439,15 @@ class Stylesheet
$query = "//";
// Will contain :before and :after
- $pseudo_elements = array();
+ $pseudo_elements = [];
// Will contain :link, etc
- $pseudo_classes = array();
+ $pseudo_classes = [];
// Parse the selector
//$s = preg_split("/([ :>.#+])/", $selector, -1, PREG_SPLIT_DELIM_CAPTURE);
- $delimiters = array(" ", ">", ".", "#", "+", ":", "[", "(");
+ $delimiters = [" ", ">", ".", "#", "+", "~", ":", "[", "("];
// Add an implicit * at the beginning of the selector
// if it begins with an attribute selector
@@ -562,20 +542,34 @@ class Stylesheet
// class=".* $tok .*" and class=".* $tok"
// This doesn't work because libxml only supports XPath 1.0...
- //$query .= "[matches(@$attr,\"^${tok}\$|^${tok}[ ]+|[ ]+${tok}\$|[ ]+${tok}[ ]+\")]";
+ //$query .= "[matches(@$attr,\"^{$tok}\$|^{$tok}[ ]+|[ ]+{$tok}\$|[ ]+{$tok}[ ]+\")]";
- // Query improvement by Michael Sheakoski :
- $query .= "[contains(concat(' ', @$attr, ' '), concat(' ', '$tok', ' '))]";
+ $query .= "[contains(concat(' ', normalize-space(@$attr), ' '), concat(' ', '$tok', ' '))]";
$tok = "";
break;
case "+":
- // All sibling elements that follow the current token
+ case "~":
+ // Next-sibling combinator
+ // Subsequent-sibling combinator
+ // https://www.w3.org/TR/selectors-3/#sibling-combinators
if (mb_substr($query, -1, 1) !== "/") {
$query .= "/";
}
+ // Tag names are case-insensitive
+ $tok = strtolower($tok);
+
+ if (!$tok) {
+ $tok = "*";
+ }
+
$query .= "following-sibling::$tok";
+
+ if ($s === "+") {
+ $query .= "[1]";
+ }
+
$tok = "";
break;
@@ -591,7 +585,7 @@ class Stylesheet
switch ($tok) {
case "first-child":
- $query .= "[1]";
+ $query .= "[not(preceding-sibling::*)]";
$tok = "";
break;
@@ -624,16 +618,17 @@ class Stylesheet
$pseudo_classes[$tok] = true;
$p = $i + 1;
$nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i) - $p));
+ $position = $last ? "(last()-position()+1)" : "position()";
// 1
if (preg_match("/^\d+$/", $nth)) {
- $condition = "position() = $nth";
+ $condition = "$position = $nth";
} // odd
elseif ($nth === "odd") {
- $condition = "(position() mod 2) = 1";
+ $condition = "($position mod 2) = 1";
} // even
elseif ($nth === "even") {
- $condition = "(position() mod 2) = 0";
+ $condition = "($position mod 2) = 0";
} // an+b
else {
$condition = $this->_selector_an_plus_b($nth, $last);
@@ -655,16 +650,17 @@ class Stylesheet
$pseudo_classes[$tok] = true;
$p = $i + 1;
$nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i) - $p));
+ $position = $last ? "(last()-position()+1)" : "position()";
// 1
if (preg_match("/^\d+$/", $nth)) {
- $condition = "position() = $nth";
+ $condition = "$position = $nth";
} // odd
elseif ($nth === "odd") {
- $condition = "(position() mod 2) = 1";
+ $condition = "($position mod 2) = 1";
} // even
elseif ($nth === "even") {
- $condition = "(position() mod 2) = 0";
+ $condition = "($position mod 2) = 0";
} // an+b
else {
$condition = $this->_selector_an_plus_b($nth, $last);
@@ -757,7 +753,8 @@ class Stylesheet
case "[":
// Attribute selectors. All with an attribute matching the following token(s)
- $attr_delimiters = array("=", "]", "~", "|", "$", "^", "*");
+ // https://www.w3.org/TR/selectors-3/#attribute-selectors
+ $attr_delimiters = ["=", "]", "~", "|", "$", "^", "*"];
$tok_len = mb_strlen($tok);
$j = 0;
@@ -824,14 +821,9 @@ class Stylesheet
case "~=":
// FIXME: this will break if $value contains quoted strings
// (e.g. [type~="a b c" "d e f"])
- $values = explode(" ", $value);
- $query .= "[";
-
- foreach ($values as $val) {
- $query .= "@$attr=\"$val\" or ";
- }
-
- $query = rtrim($query, " or ") . "]";
+ // FIXME: Don't match anything if value contains
+ // whitespace or is the empty string
+ $query .= "[contains(concat(' ', normalize-space(@$attr), ' '), concat(' ', '$value', ' '))]";
break;
case "|=":
@@ -889,32 +881,33 @@ class Stylesheet
$query = rtrim($query, "/");
}
- return array("query" => $query, "pseudo_elements" => $pseudo_elements);
+ return ['query' => $query, 'pseudo_elements' => $pseudo_elements];
}
/**
* https://github.com/tenderlove/nokogiri/blob/master/lib/nokogiri/css/xpath_visitor.rb
*
- * @param $expr
+ * @param string $expr
* @param bool $last
+ *
* @return string
*/
- protected function _selector_an_plus_b($expr, $last = false)
+ protected function _selector_an_plus_b(string $expr, bool $last = false): string
{
$expr = preg_replace("/\s/", "", $expr);
if (!preg_match("/^(?P-?[0-9]*)?n(?P[-+]?[0-9]+)?$/", $expr, $matches)) {
return "false()";
}
- $a = ((isset($matches["a"]) && $matches["a"] !== "") ? intval($matches["a"]) : 1);
- $b = ((isset($matches["b"]) && $matches["b"] !== "") ? intval($matches["b"]) : 0);
+ $a = (isset($matches["a"]) && $matches["a"] !== "") ? ($matches["a"] !== "-" ? intval($matches["a"]) : -1) : 1;
+ $b = (isset($matches["b"]) && $matches["b"] !== "") ? intval($matches["b"]) : 0;
- $position = ($last ? "(last()-position()+1)" : "position()");
+ $position = $last ? "(last()-position()+1)" : "position()";
if ($b == 0) {
return "($position mod $a) = 0";
} else {
- $compare = (($a < 0) ? "<=" : ">=");
+ $compare = ($a < 0) ? "<=" : ">=";
$b2 = -$b;
if ($b2 >= 0) {
$b2 = "+$b2";
@@ -946,7 +939,7 @@ class Stylesheet
// FIXME: this is not particularly robust...
- $styles = array();
+ $styles = [];
$xp = new DOMXPath($tree->get_dom());
$DEBUGCSS = $this->_dompdf->getOptions()->getDebugCss();
@@ -963,7 +956,7 @@ class Stylesheet
// Retrieve the nodes, limit to body for generated content
//TODO: If we use a context node can we remove the leading dot?
$nodes = @$xp->query('.' . $query["query"]);
- if ($nodes == null) {
+ if ($nodes === false) {
Helpers::record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__);
continue;
}
@@ -981,7 +974,16 @@ class Stylesheet
continue;
}
- if (($src = $this->_image($style->get_prop('content'))) !== "none") {
+ $content = $style->get_specified("content");
+
+ // Do not create non-displayed before/after pseudo elements
+ // https://www.w3.org/TR/CSS21/generate.html#content
+ // https://www.w3.org/TR/CSS21/generate.html#undisplayed-counters
+ if ($content === "normal" || $content === "none") {
+ continue;
+ }
+
+ if (($src = $this->resolve_url($content)) !== "none") {
$new_node = $node->ownerDocument->createElement("img_generated");
$new_node->setAttribute("src", $src);
} else {
@@ -1004,7 +1006,7 @@ class Stylesheet
// Retrieve the nodes
$nodes = @$xp->query($query["query"]);
- if ($nodes == null) {
+ if ($nodes === false) {
Helpers::record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__);
continue;
}
@@ -1041,7 +1043,7 @@ class Stylesheet
// Now create the styles and assign them to the appropriate frames. (We
// iterate over the tree using an implicit FrameTree iterator.)
$root_flg = false;
- foreach ($tree->get_frames() as $frame) {
+ foreach ($tree as $frame) {
// Helpers::pre_r($frame->get_node()->nodeName . ":");
if (!$root_flg && $this->_page_styles["base"]) {
$style = $this->_page_styles["base"];
@@ -1052,18 +1054,15 @@ class Stylesheet
// Find nearest DOMElement parent
$p = $frame;
while ($p = $p->get_parent()) {
- if ($p->get_node()->nodeType == XML_ELEMENT_NODE) {
+ if ($p->get_node()->nodeType === XML_ELEMENT_NODE) {
break;
}
}
// Styles can only be applied directly to DOMElements; anonymous
// frames inherit from their parent
- if ($frame->get_node()->nodeType != XML_ELEMENT_NODE) {
- if ($p) {
- $style->inherit($p->get_style());
- }
-
+ if ($frame->get_node()->nodeType !== XML_ELEMENT_NODE) {
+ $style->inherit($p ? $p->get_style() : null);
$frame->set_style($style);
continue;
}
@@ -1089,21 +1088,21 @@ class Stylesheet
if (isset($styles[$id])) {
/** @var array[][] $applied_styles */
- $applied_styles = $styles[$frame->get_id()];
+ $applied_styles = $styles[$id];
// Sort by specificity
ksort($applied_styles);
if ($DEBUGCSS) {
$debug_nodename = $frame->get_node()->nodeName;
- print "\n[$debug_nodename\n";
+ print "\n$debug_nodename [\n";
foreach ($applied_styles as $spec => $arr) {
- printf("specificity: 0x%08x\n", $spec);
+ printf(" specificity 0x%08x\n", $spec);
/** @var Style $s */
foreach ($arr as $s) {
- print "[\n";
+ print " [\n";
$s->debug_print();
- print "]\n";
+ print " ]\n";
}
}
}
@@ -1174,32 +1173,23 @@ class Stylesheet
}
}
- // Inherit parent's styles if required
- if ($p) {
-
- if ($DEBUGCSS) {
- print "inherit:\n";
- print "[\n";
- $p->get_style()->debug_print();
- print "]\n";
- }
-
- $style->inherit($p->get_style());
+ // Handle inheritance
+ if ($p && $DEBUGCSS) {
+ print " inherit [\n";
+ $p->get_style()->debug_print();
+ print " ]\n";
}
+ $style->inherit($p ? $p->get_style() : null);
+
if ($DEBUGCSS) {
- print "DomElementStyle:\n";
- print "[\n";
+ print " DomElementStyle [\n";
$style->debug_print();
- print "]\n";
- print "/$debug_nodename]\n ";
+ print " ]\n";
+ print "]\n ";
}
- /*DEBUGCSS print: see below different print debugging method
- Helpers::pre_r($frame->get_node()->nodeName . ":");
- echo "";
- echo $style;
- echo " ";*/
+ $style->clear_important();
$frame->set_style($style);
if (!$root_flg && $this->_page_styles["base"]) {
@@ -1221,7 +1211,6 @@ class Stylesheet
$this->_styles[$key] = null;
unset($this->_styles[$key]);
}
-
}
/**
@@ -1234,15 +1223,14 @@ class Stylesheet
*/
private function _parse_css($str)
{
-
$str = trim($str);
// Destroy comments and remove HTML comments
- $css = preg_replace(array(
+ $css = preg_replace([
"'/\*.*?\*/'si",
"/^$/"
- ), "", $str);
+ ], "", $str);
// FIXME: handle '{' within strings, e.g. [attr="string {}"]
@@ -1304,16 +1292,16 @@ class Stylesheet
} elseif (!in_array($media_query, self::$VALID_MEDIA_TYPES)) {
// otherwise conditionally parse the stylesheet assuming there are parseable media queries
if (preg_match_all($media_query_regex, $media_query, $media_query_matches, PREG_SET_ORDER) !== false) {
- $mq = array();
+ $mq = [];
foreach ($media_query_matches as $media_query_match) {
if (empty($media_query_match[1]) === false) {
$media_query_feature = strtolower($media_query_match[3]);
$media_query_value = strtolower($media_query_match[2]);
- $mq[] = array($media_query_feature, $media_query_value);
- } else if (empty($media_query_match[4]) === false) {
+ $mq[] = [$media_query_feature, $media_query_value];
+ } elseif (empty($media_query_match[4]) === false) {
$media_query_feature = strtolower($media_query_match[5]);
$media_query_value = (array_key_exists(8, $media_query_match) ? strtolower($media_query_match[8]) : null);
- $mq[] = array($media_query_feature, $media_query_value);
+ $mq[] = [$media_query_feature, $media_query_value];
}
}
$this->_parse_sections($match[5], $mq);
@@ -1359,6 +1347,7 @@ class Stylesheet
/** @noinspection PhpMissingBreakStatementInspection */
case ":first":
$key = $page_selector;
+ break;
default:
break 2;
@@ -1387,57 +1376,44 @@ class Stylesheet
if ($match[7] !== "") {
$this->_parse_sections($match[7]);
}
-
}
}
/**
- * See also style.cls Style::_image(), refactoring?, works also for imported css files
+ * Resolve the given `url()` declaration to an absolute URL.
*
- * @param $val
- * @return string
+ * @param string|null $val The declaration to resolve in the context of the stylesheet.
+ * @return string The resolved URL, or `none`, if the value is `none`,
+ * invalid, or points to a non-existent local file.
*/
- protected function _image($val)
+ public function resolve_url($val): string
{
$DEBUGCSS = $this->_dompdf->getOptions()->getDebugCss();
$parsed_url = "none";
- if (mb_strpos($val, "url") === false) {
+ if (empty($val) || $val === "none") {
+ $path = "none";
+ } elseif (mb_strpos($val, "url") === false) {
$path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none
} else {
$val = preg_replace("/url\(\s*['\"]?([^'\")]+)['\"]?\s*\)/", "\\1", trim($val));
// Resolve the url now in the context of the current stylesheet
- $parsed_url = Helpers::explode_url($val);
- if ($parsed_url["protocol"] == "" && $this->get_protocol() == "") {
- if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\') {
- $path = $_SERVER["DOCUMENT_ROOT"] . '/';
- } else {
- $path = $this->get_base_path();
- }
-
- $path .= $parsed_url["path"] . $parsed_url["file"];
- $path = realpath($path);
- // If realpath returns FALSE then specifically state that there is no background image
- // FIXME: Is this causing problems for imported CSS files? There are some './none' references when running the test cases.
- if (!$path) {
- $path = 'none';
- }
- } else {
- $path = Helpers::build_url($this->get_protocol(),
- $this->get_host(),
- $this->get_base_path(),
- $val);
+ $path = Helpers::build_url($this->_protocol,
+ $this->_base_host,
+ $this->_base_path,
+ $val);
+ if ($path === null) {
+ $path = "none";
}
}
-
if ($DEBUGCSS) {
+ $parsed_url = Helpers::explode_url($path);
print "[_image\n";
print_r($parsed_url);
- print $this->get_protocol() . "\n" . $this->get_base_path() . "\n" . $path . "\n";
- print "_image] ";;
+ print $this->_protocol . "\n" . $this->_base_path . "\n" . $path . "\n";
+ print "_image]";
}
-
return $path;
}
@@ -1477,20 +1453,19 @@ class Stylesheet
// $url = str_replace(array('"',"url", "(", ")"), "", $url);
// If the protocol is php, assume that we will import using file://
- // $url = Helpers::build_url($protocol == "php://" ? "file://" : $protocol, $host, $path, $url);
+ // $url = Helpers::build_url($protocol === "php://" ? "file://" : $protocol, $host, $path, $url);
// Above does not work for subfolders and absolute urls.
// Todo: As above, do we need to replace php or file to an empty protocol for local files?
- $url = $this->_image($url);
-
- $this->load_css_file($url);
+ if (($url = $this->resolve_url($url)) !== "none") {
+ $this->load_css_file($url);
+ }
// Restore the current base url
$this->_protocol = $protocol;
$this->_base_host = $host;
$this->_base_path = $path;
}
-
}
/**
@@ -1503,24 +1478,20 @@ class Stylesheet
{
$descriptors = $this->_parse_properties($str);
- preg_match_all("/(url|local)\s*\([\"\']?([^\"\'\)]+)[\"\']?\)\s*(format\s*\([\"\']?([^\"\'\)]+)[\"\']?\))?/i", $descriptors->src, $src);
-
- $sources = array();
- $valid_sources = array();
+ preg_match_all("/(url|local)\s*\(\s*[\"\']?([^\"\'\)]+)[\"\']?\s*\)\s*(format\s*\(\s*[\"\']?([^\"\'\)]+)[\"\']?\s*\))?/i", $descriptors->src, $src);
+ $valid_sources = [];
foreach ($src[0] as $i => $value) {
- $source = array(
+ $source = [
"local" => strtolower($src[1][$i]) === "local",
"uri" => $src[2][$i],
"format" => strtolower($src[4][$i]),
"path" => Helpers::build_url($this->_protocol, $this->_base_host, $this->_base_path, $src[2][$i]),
- );
+ ];
- if (!$source["local"] && in_array($source["format"], array("", "truetype"))) {
+ if (!$source["local"] && in_array($source["format"], ["", "truetype"]) && $source["path"] !== null) {
$valid_sources[] = $source;
}
-
- $sources[] = $source;
}
// No valid sources
@@ -1528,11 +1499,11 @@ class Stylesheet
return;
}
- $style = array(
+ $style = [
"family" => $descriptors->get_font_family_raw(),
"weight" => $descriptors->font_weight,
"style" => $descriptors->font_style,
- );
+ ];
$this->getFontMetrics()->registerFont($style, $valid_sources[0]["path"], $this->_dompdf->getHttpContext());
}
@@ -1565,14 +1536,9 @@ class Stylesheet
if (preg_match("/([a-z-]+)\s*:\s*[^:]+$/i", $prop, $m))
$prop = $m[0];
}*/
+
//A css property can have " ! important" appended (whitespace optional)
//strip this off to decode core of the property correctly.
- //Pass on in the style to allow proper handling:
- //!important properties can only be overridden by other !important ones.
- //$style->$prop_name = is a shortcut of $style->__set($prop_name,$value);.
- //If no specific set function available, set _props["prop_name"]
- //style is always copied completely, or $_props handled separately
- //Therefore set a _important_props["prop_name"]=true to indicate the modifier
/* Instead of short code, prefer the typical case with fast code
$important = preg_match("/(.*?)!\s*important/",$prop,$match);
@@ -1608,19 +1574,10 @@ class Stylesheet
$prop_name = rtrim(mb_strtolower(mb_substr($prop, 0, $i)));
$value = ltrim(mb_substr($prop, $i + 1));
+
if ($DEBUGCSS) print $prop_name . ':=' . $value . ($important ? '!IMPORTANT' : '') . ')';
- //New style, anyway empty
- //if ($important || !$style->important_get($prop_name) ) {
- //$style->$prop_name = array($value,$important);
- //assignment might be replaced by overloading through __set,
- //and overloaded functions might check _important_props,
- //therefore set _important_props first.
- if ($important) {
- $style->important_set($prop_name);
- }
- //For easier debugging, don't use overloading of assignments with __set
- $style->$prop_name = $value;
- //$style->props_set($prop_name, $value);
+
+ $style->set_prop($prop_name, $value, $important, false);
}
if ($DEBUGCSS) print '_parse_properties]';
@@ -1633,14 +1590,12 @@ class Stylesheet
* @param string $str CSS selectors and rulesets
* @param array $media_queries
*/
- private function _parse_sections($str, $media_queries = array())
+ private function _parse_sections($str, $media_queries = [])
{
- // Pre-process: collapse all whitespace and strip whitespace around '>',
- // '.', ':', '+', '#'
-
- $patterns = array("/[\\s\n]+/", "/\\s+([>.:+#])\\s+/");
- $replacements = array(" ", "\\1");
- $str = preg_replace($patterns, $replacements, $str);
+ // Pre-process selectors: collapse all whitespace and strip whitespace
+ // around '>', '.', ':', '+', '~', '#'
+ $patterns = ["/\s+/", "/\s+([>.:+~#])\s+/"];
+ $replacements = [" ", "\\1"];
$DEBUGCSS = $this->_dompdf->getOptions()->getDebugCss();
$sections = explode("}", $str);
@@ -1649,10 +1604,10 @@ class Stylesheet
$i = mb_strpos($sect, "{");
if ($i === false) { continue; }
- //$selectors = explode(",", mb_substr($sect, 0, $i));
- $selectors = preg_split("/,(?![^\(]*\))/", mb_substr($sect, 0, $i),0, PREG_SPLIT_NO_EMPTY);
if ($DEBUGCSS) print '[section';
+ $selector_str = preg_replace($patterns, $replacements, mb_substr($sect, 0, $i));
+ $selectors = preg_split("/,(?![^\(]*\))/", $selector_str, 0, PREG_SPLIT_NO_EMPTY);
$style = $this->_parse_properties(trim(mb_substr($sect, $i + 1)));
// Assign it to the selected elements
@@ -1679,17 +1634,18 @@ class Stylesheet
}
if ($DEBUGCSS) {
- print '_parse_sections]';
+ print "_parse_sections]\n";
}
}
/**
* @return string
*/
- public static function getDefaultStylesheet()
+ public function getDefaultStylesheet()
{
- $dir = realpath(__DIR__ . "/../..");
- return $dir . self::DEFAULT_STYLESHEET;
+ $options = $this->_dompdf->getOptions();
+ $rootDir = realpath($options->getRootDir());
+ return Helpers::build_url("file://", "", $rootDir, $rootDir . self::DEFAULT_STYLESHEET);
}
/**
diff --git a/library/vendor/dompdf/src/Dompdf.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Dompdf.php
similarity index 66%
rename from library/vendor/dompdf/src/Dompdf.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Dompdf.php
index 90d26ae22..6feec59a6 100644
--- a/library/vendor/dompdf/src/Dompdf.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Dompdf.php
@@ -1,9 +1,7 @@
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf;
@@ -14,11 +12,10 @@ use Dompdf\Adapter\CPDF;
use DOMXPath;
use Dompdf\Frame\Factory;
use Dompdf\Frame\FrameTree;
-use HTML5_Tokenizer;
-use HTML5_TreeBuilder;
use Dompdf\Image\Cache;
-use Dompdf\Renderer\ListBullet;
use Dompdf\Css\Stylesheet;
+use Dompdf\Helpers;
+use Masterminds\HTML5;
/**
* Dompdf - PHP5 HTML to PDF renderer
@@ -123,7 +120,7 @@ class Dompdf
*
* @var array
*/
- private $callbacks = array();
+ private $callbacks = [];
/**
* Experimental caching capability
@@ -149,26 +146,11 @@ class Dompdf
private $basePath = "";
/**
- * Protcol used to request file (file://, http://, etc)
+ * Protocol used to request file (file://, http://, etc)
*
* @var string
*/
- private $protocol;
-
- /**
- * HTTP context created with stream_context_create()
- * Will be used for file_get_contents
- *
- * @var resource
- */
- private $httpContext;
-
- /**
- * Timestamp of the script start time
- *
- * @var int
- */
- private $startTime = null;
+ private $protocol = "";
/**
* The system's locale
@@ -178,11 +160,18 @@ class Dompdf
private $systemLocale = null;
/**
- * Tells if the system's locale is the C standard one
+ * The system's mbstring internal encoding
*
- * @var bool
+ * @var string
*/
- private $localeStandard = false;
+ private $mbstringEncoding = null;
+
+ /**
+ * The system's PCRE JIT configuration
+ *
+ * @var string
+ */
+ private $pcreJit = null;
/**
* The default view of the PDF in the viewer
@@ -196,25 +185,15 @@ class Dompdf
*
* @var array
*/
- private $defaultViewOptions = array();
+ private $defaultViewOptions = [];
/**
- * Tells wether the DOM document is in quirksmode (experimental)
+ * Tells whether the DOM document is in quirksmode (experimental)
*
* @var bool
*/
private $quirksmode = false;
- /**
- * Protocol whitelist
- *
- * Protocols and PHP wrappers allowed in URLs. Full support is not
- * guarantee for the protocols/wrappers contained in this array.
- *
- * @var array
- */
- private $allowedProtocols = array(null, "", "file://", "http://", "https://");
-
/**
* Local file extension whitelist
*
@@ -222,12 +201,12 @@ class Dompdf
*
* @var array
*/
- private $allowedLocalFileExtensions = array("htm", "html");
+ private $allowedLocalFileExtensions = ["htm", "html"];
/**
* @var array
*/
- private $messages = array();
+ private $messages = [];
/**
* @var Options
@@ -245,39 +224,32 @@ class Dompdf
* @var array
* @deprecated
*/
- public static $native_fonts = array(
+ public static $native_fonts = [
"courier", "courier-bold", "courier-oblique", "courier-boldoblique",
"helvetica", "helvetica-bold", "helvetica-oblique", "helvetica-boldoblique",
"times-roman", "times-bold", "times-italic", "times-bolditalic",
"symbol", "zapfdinbats"
- );
+ ];
/**
* The list of built-in fonts
*
* @var array
*/
- public static $nativeFonts = array(
+ public static $nativeFonts = [
"courier", "courier-bold", "courier-oblique", "courier-boldoblique",
"helvetica", "helvetica-bold", "helvetica-oblique", "helvetica-boldoblique",
"times-roman", "times-bold", "times-italic", "times-bolditalic",
"symbol", "zapfdinbats"
- );
+ ];
/**
* Class constructor
*
- * @param array|Options $options
+ * @param Options|array|null $options
*/
public function __construct($options = null)
{
- mb_internal_encoding('UTF-8');
-
- if (version_compare(PHP_VERSION, '7.0.0') >= 0)
- {
- ini_set('pcre.jit', 0);
- }
-
if (isset($options) && $options instanceof Options) {
$this->setOptions($options);
} elseif (is_array($options)) {
@@ -287,46 +259,62 @@ class Dompdf
}
$versionFile = realpath(__DIR__ . '/../VERSION');
- if (file_exists($versionFile) && ($version = file_get_contents($versionFile)) !== false && $version !== '$Format:<%h>$') {
- $this->version = sprintf('dompdf %s', $version);
+ if (($version = file_get_contents($versionFile)) !== false) {
+ $version = trim($version);
+ if ($version !== '$Format:<%h>$') {
+ $this->version = sprintf('dompdf %s', $version);
+ }
}
- $this->localeStandard = sprintf('%.1f', 1.0) == '1.0';
- $this->saveLocale();
+ $this->setPhpConfig();
+
$this->paperSize = $this->options->getDefaultPaperSize();
$this->paperOrientation = $this->options->getDefaultPaperOrientation();
- $this->setCanvas(CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation));
- $this->setFontMetrics(new FontMetrics($this->getCanvas(), $this->getOptions()));
+ $this->canvas = CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation);
+ $this->fontMetrics = new FontMetrics($this->canvas, $this->options);
$this->css = new Stylesheet($this);
- $this->restoreLocale();
+ $this->restorePhpConfig();
}
/**
- * Save the system's locale configuration and
- * set the right value for numeric formatting
+ * Save the system's existing locale, PCRE JIT, and MBString encoding
+ * configuration and configure the system for Dompdf processing
*/
- private function saveLocale()
+ private function setPhpConfig()
{
- if ($this->localeStandard) {
- return;
+ if (sprintf('%.1f', 1.0) !== '1.0') {
+ $this->systemLocale = setlocale(LC_NUMERIC, "0");
+ setlocale(LC_NUMERIC, "C");
}
- $this->systemLocale = setlocale(LC_NUMERIC, "0");
- setlocale(LC_NUMERIC, "C");
+ $this->pcreJit = @ini_get('pcre.jit');
+ @ini_set('pcre.jit', '0');
+
+ $this->mbstringEncoding = mb_internal_encoding();
+ mb_internal_encoding('UTF-8');
}
/**
* Restore the system's locale configuration
*/
- private function restoreLocale()
+ private function restorePhpConfig()
{
- if ($this->localeStandard) {
- return;
+ if ($this->systemLocale !== null) {
+ setlocale(LC_NUMERIC, $this->systemLocale);
+ $this->systemLocale = null;
}
- setlocale(LC_NUMERIC, $this->systemLocale);
+ if ($this->pcreJit !== null) {
+ @ini_set('pcre.jit', $this->pcreJit);
+ $this->pcreJit = null;
+ }
+
+ if ($this->mbstringEncoding !== null) {
+ mb_internal_encoding($this->mbstringEncoding);
+ $this->mbstringEncoding = null;
+ }
}
/**
@@ -343,48 +331,43 @@ class Dompdf
* Parse errors are stored in the global array _dompdf_warnings.
*
* @param string $file a filename or url to load
+ * @param string $encoding Encoding of $file
*
* @throws Exception
*/
- public function loadHtmlFile($file)
+ public function loadHtmlFile($file, $encoding = null)
{
- $this->saveLocale();
+ $this->setPhpConfig();
if (!$this->protocol && !$this->baseHost && !$this->basePath) {
- list($this->protocol, $this->baseHost, $this->basePath) = Helpers::explode_url($file);
+ [$this->protocol, $this->baseHost, $this->basePath] = Helpers::explode_url($file);
}
$protocol = strtolower($this->protocol);
+ $uri = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $file);
- if ( !in_array($protocol, $this->allowedProtocols) ) {
+ $allowed_protocols = $this->options->getAllowedProtocols();
+ if (!array_key_exists($protocol, $allowed_protocols)) {
throw new Exception("Permission denied on $file. The communication protocol is not supported.");
}
- if (!$this->options->isRemoteEnabled() && ($protocol != "" && $protocol !== "file://")) {
- throw new Exception("Remote file requested, but remote file download is disabled.");
- }
-
- if ($protocol == "" || $protocol === "file://") {
- $realfile = realpath($file);
-
- $chroot = realpath($this->options->getChroot());
- if ($chroot && strpos($realfile, $chroot) !== 0) {
- throw new Exception("Permission denied on $file. The file could not be found under the directory specified by Options::chroot.");
- }
-
- $ext = strtolower(pathinfo($realfile, PATHINFO_EXTENSION));
+ if ($protocol === "file://") {
+ $ext = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
if (!in_array($ext, $this->allowedLocalFileExtensions)) {
- throw new Exception("Permission denied on $file. This file extension is forbidden");
+ throw new Exception("Permission denied on $file: The file extension is forbidden.");
}
-
- if (!$realfile) {
- throw new Exception("File '$file' not found.");
- }
-
- $file = $realfile;
}
- list($contents, $http_response_header) = Helpers::getFileContent($file, $this->httpContext);
- $encoding = 'UTF-8';
+ foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
+ [$result, $message] = $rule($uri);
+ if (!$result) {
+ throw new Exception("Error loading $file: $message");
+ }
+ }
+
+ [$contents, $http_response_header] = Helpers::getFileContent($uri, $this->options->getHttpContext());
+ if ($contents === null) {
+ throw new Exception("File '$file' not found.");
+ }
// See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/
if (isset($http_response_header)) {
@@ -396,7 +379,7 @@ class Dompdf
}
}
- $this->restoreLocale();
+ $this->restorePhpConfig();
$this->loadHtml($contents, $encoding);
}
@@ -406,55 +389,75 @@ class Dompdf
* @param string $encoding
* @deprecated
*/
- public function load_html($str, $encoding = 'UTF-8')
+ public function load_html($str, $encoding = null)
{
$this->loadHtml($str, $encoding);
}
+ public function loadDOM($doc, $quirksmode = false) {
+ // Remove #text children nodes in nodes that shouldn't have
+ $tag_names = ["html", "head", "table", "tbody", "thead", "tfoot", "tr"];
+ foreach ($tag_names as $tag_name) {
+ $nodes = $doc->getElementsByTagName($tag_name);
+
+ foreach ($nodes as $node) {
+ self::removeTextNodes($node);
+ }
+ }
+
+ $this->dom = $doc;
+ $this->quirksmode = $quirksmode;
+ $this->tree = new FrameTree($this->dom);
+ }
+
/**
* Loads an HTML string
* Parse errors are stored in the global array _dompdf_warnings.
- * @todo use the $encoding variable
*
* @param string $str HTML text to load
- * @param string $encoding Not used yet
+ * @param string $encoding Encoding of $str
*/
- public function loadHtml($str, $encoding = 'UTF-8')
+ public function loadHtml($str, $encoding = null)
{
- $this->saveLocale();
+ $this->setPhpConfig();
- // FIXME: Determine character encoding, switch to UTF8, update meta tag. Need better http/file stream encoding detection, currently relies on text or meta tag.
- $known_encodings = mb_list_encodings();
- mb_detect_order('auto');
- if (($file_encoding = mb_detect_encoding($str, null, true)) === false) {
- $file_encoding = "auto";
- }
- if (in_array(strtoupper($file_encoding), array('UTF-8','UTF8')) === false) {
- $str = mb_convert_encoding($str, 'UTF-8', $file_encoding);
+ // Determine character encoding when $encoding parameter not used
+ if ($encoding === null) {
+ mb_detect_order('auto');
+ if (($encoding = mb_detect_encoding($str, null, true)) === false) {
+
+ //"auto" is expanded to "ASCII,JIS,UTF-8,EUC-JP,SJIS"
+ $encoding = "auto";
+ }
}
- $metatags = array(
+ if (in_array(strtoupper($encoding), array('UTF-8','UTF8')) === false) {
+ $str = mb_convert_encoding($str, 'UTF-8', $encoding);
+
+ //Update encoding after converting
+ $encoding = 'UTF-8';
+ }
+
+ $metatags = [
'@ ]*charset\s*=\s*["\']?\s*([^"\' ]+)@i',
- );
+ ];
foreach ($metatags as $metatag) {
if (preg_match($metatag, $str, $matches)) {
- if (isset($matches[1]) && in_array($matches[1], $known_encodings)) {
+ if (isset($matches[1]) && in_array($matches[1], mb_list_encodings())) {
$document_encoding = $matches[1];
break;
}
}
}
- if (isset($document_encoding) && in_array(strtoupper($document_encoding), array('UTF-8','UTF8')) === false) {
+ if (isset($document_encoding) && in_array(strtoupper($document_encoding), ['UTF-8','UTF8']) === false) {
$str = preg_replace('/charset=([^\s"]+)/i', 'charset=UTF-8', $str);
} elseif (isset($document_encoding) === false && strpos($str, '') !== false) {
$str = str_replace('', ' ', $str);
} elseif (isset($document_encoding) === false) {
$str = ' ' . $str;
}
- //FIXME: since we're not using this just yet
- $encoding = 'UTF-8';
// remove BOM mark from UTF-8, it's treated as document text by DOMDocument
// 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)?
@@ -463,75 +466,28 @@ class Dompdf
}
// Store parsing warnings as messages
- set_error_handler(array("\\Dompdf\\Helpers", "record_warnings"));
+ set_error_handler([Helpers::class, 'record_warnings']);
- // @todo Take the quirksmode into account
- // http://hsivonen.iki.fi/doctype/
- // https://developer.mozilla.org/en/mozilla's_quirks_mode
- $quirksmode = false;
+ try {
+ // @todo Take the quirksmode into account
+ // https://quirks.spec.whatwg.org/
+ // http://hsivonen.iki.fi/doctype/
+ $quirksmode = false;
- if ($this->options->isHtml5ParserEnabled() && class_exists("HTML5_Tokenizer")) {
- $tokenizer = new HTML5_Tokenizer($str);
- $tokenizer->parse();
- $doc = $tokenizer->save();
+ $html5 = new HTML5(['encoding' => $encoding, 'disable_html_ns' => true]);
+ $dom = $html5->loadHTML($str);
- // Remove #text children nodes in nodes that shouldn't have
- $tag_names = array("html", "table", "tbody", "thead", "tfoot", "tr");
- foreach ($tag_names as $tag_name) {
- $nodes = $doc->getElementsByTagName($tag_name);
-
- foreach ($nodes as $node) {
- self::removeTextNodes($node);
- }
- }
-
- $quirksmode = ($tokenizer->getTree()->getQuirksMode() > HTML5_TreeBuilder::NO_QUIRKS);
- } else {
- // loadHTML assumes ISO-8859-1 unless otherwise specified on the HTML document header.
- // http://devzone.zend.com/1538/php-dom-xml-extension-encoding-processing/ (see #4)
- // http://stackoverflow.com/a/11310258/264628
+ // extra step to normalize the HTML document structure
+ // see Masterminds/html5-php#166
$doc = new DOMDocument("1.0", $encoding);
$doc->preserveWhiteSpace = true;
- $doc->loadHTML($str);
- $doc->encoding = $encoding;
+ $doc->loadHTML($html5->saveHTML($dom), LIBXML_NOWARNING | LIBXML_NOERROR);
- // Remove #text children nodes in nodes that shouldn't have
- $tag_names = array("html", "table", "tbody", "thead", "tfoot", "tr");
- foreach ($tag_names as $tag_name) {
- $nodes = $doc->getElementsByTagName($tag_name);
-
- foreach ($nodes as $node) {
- self::removeTextNodes($node);
- }
- }
-
- // If some text is before the doctype, we are in quirksmode
- if (preg_match("/^(.+)
- if (!$doc->doctype->publicId && !$doc->doctype->systemId) {
- $quirksmode = false;
- }
-
- // not XHTML
- if (!preg_match("/xhtml/i", $doc->doctype->publicId)) {
- $quirksmode = true;
- }
- }
+ $this->loadDOM($doc, $quirksmode);
+ } finally {
+ restore_error_handler();
+ $this->restorePhpConfig();
}
-
- $this->dom = $doc;
- $this->quirksmode = $quirksmode;
-
- $this->tree = new FrameTree($this->dom);
-
- restore_error_handler();
-
- $this->restoreLocale();
}
/**
@@ -548,7 +504,7 @@ class Dompdf
*/
public static function removeTextNodes(DOMNode $node)
{
- $children = array();
+ $children = [];
for ($i = 0; $i < $node->childNodes->length; $i++) {
$child = $node->childNodes->item($i);
if ($child->nodeName === "#text") {
@@ -569,15 +525,17 @@ class Dompdf
{
$this->tree->build_tree();
- $this->css->load_css_file(Stylesheet::getDefaultStylesheet(), Stylesheet::ORIG_UA);
+ $this->css->load_css_file($this->css->getDefaultStylesheet(), Stylesheet::ORIG_UA);
$acceptedmedia = Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES;
$acceptedmedia[] = $this->options->getDefaultMediaType();
//
- $base_nodes = $this->dom->getElementsByTagName("base");
- if ($base_nodes->length && ($href = $base_nodes->item(0)->getAttribute("href"))) {
- list($this->protocol, $this->baseHost, $this->basePath) = Helpers::explode_url($href);
+ /** @var \DOMElement|null */
+ $baseNode = $this->dom->getElementsByTagName("base")->item(0);
+ $baseHref = $baseNode ? $baseNode->getAttribute("href") : "";
+ if ($baseHref !== "") {
+ [$this->protocol, $this->baseHost, $this->basePath] = Helpers::explode_url($baseHref);
}
// Set the base path of the Stylesheet to that of the file being processed
@@ -619,7 +577,9 @@ class Dompdf
$url = $tag->getAttribute("href");
$url = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $url);
- $this->css->load_css_file($url, Stylesheet::ORIG_AUTHOR);
+ if ($url !== null) {
+ $this->css->load_css_file($url, Stylesheet::ORIG_AUTHOR);
+ }
}
break;
@@ -647,9 +607,19 @@ class Dompdf
$css = $tag->nodeValue;
}
+ // Set the base path of the Stylesheet to that of the file being processed
+ $this->css->set_protocol($this->protocol);
+ $this->css->set_host($this->baseHost);
+ $this->css->set_base_path($this->basePath);
+
$this->css->load_css($css, Stylesheet::ORIG_AUTHOR);
break;
}
+
+ // Set the base path of the Stylesheet to that of the file being processed
+ $this->css->set_protocol($this->protocol);
+ $this->css->set_host($this->baseHost);
+ $this->css->set_base_path($this->basePath);
}
}
@@ -688,7 +658,7 @@ class Dompdf
*/
public function parseDefaultView($value)
{
- $valid = array("XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV");
+ $valid = ["XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV"];
$options = preg_split("/\s*,\s*/", trim($value));
$defaultView = array_shift($options);
@@ -706,16 +676,15 @@ class Dompdf
*/
public function render()
{
- $this->saveLocale();
- $options = $this->options;
+ $this->setPhpConfig();
- $logOutputFile = $options->getLogOutputFile();
+ $logOutputFile = $this->options->getLogOutputFile();
if ($logOutputFile) {
if (!file_exists($logOutputFile) && is_writable(dirname($logOutputFile))) {
touch($logOutputFile);
}
- $this->startTime = microtime(true);
+ $startTime = microtime(true);
if (is_writable($logOutputFile)) {
ob_start();
}
@@ -734,82 +703,31 @@ class Dompdf
$pageStyle->inherit($basePageStyle);
}
- $defaultOptionPaperSize = $this->getPaperSize($options->getDefaultPaperSize());
- // If there is a CSS defined paper size compare to the paper size used to create the canvas to determine a
- // recreation need
+ // Set paper size if defined via CSS
if (is_array($basePageStyle->size)) {
- $basePageStyleSize = $basePageStyle->size;
- $this->setPaper(array(0, 0, $basePageStyleSize[0], $basePageStyleSize[1]));
+ [$width, $height] = $basePageStyle->size;
+ $this->setPaper([0, 0, $width, $height]);
}
- $paperSize = $this->getPaperSize();
- if (
- $defaultOptionPaperSize[2] !== $paperSize[2] ||
- $defaultOptionPaperSize[3] !== $paperSize[3] ||
- $options->getDefaultPaperOrientation() !== $this->paperOrientation
- ) {
- $this->setCanvas(CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation));
- $this->fontMetrics->setCanvas($this->getCanvas());
+ // Create a new canvas instance if the current one does not match the
+ // desired paper size
+ $canvasWidth = $this->canvas->get_width();
+ $canvasHeight = $this->canvas->get_height();
+ $size = $this->getPaperSize();
+
+ if ($canvasWidth !== $size[2] || $canvasHeight !== $size[3]) {
+ $this->canvas = CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation);
+ $this->fontMetrics->setCanvas($this->canvas);
}
- $canvas = $this->getCanvas();
+ $canvas = $this->canvas;
- if ($options->isFontSubsettingEnabled() && $canvas instanceof CPDF) {
- foreach ($this->tree->get_frames() as $frame) {
- $style = $frame->get_style();
- $node = $frame->get_node();
-
- // Handle text nodes
- if ($node->nodeName === "#text") {
- $chars = mb_strtoupper($node->nodeValue) . mb_strtolower($node->nodeValue);
- $canvas->register_string_subset($style->font_family, $chars);
- continue;
- }
-
- // Handle generated content (list items)
- if ($style->display === "list-item") {
- $chars = ListBullet::get_counter_chars($style->list_style_type);
- $canvas->register_string_subset($style->font_family, $chars);
- $canvas->register_string_subset($style->font_family, '.');
- continue;
- }
-
- // Handle other generated content (pseudo elements)
- // FIXME: This only captures the text of the stylesheet declaration,
- // not the actual generated content, and forces all possible counter
- // values. See notes in issue #750.
- if ($frame->get_node()->nodeName == "dompdf_generated") {
- // all possible counter values, just in case
- $chars = ListBullet::get_counter_chars('decimal');
- $canvas->register_string_subset($style->font_family, $chars);
- $chars = ListBullet::get_counter_chars('upper-alpha');
- $canvas->register_string_subset($style->font_family, $chars);
- $chars = ListBullet::get_counter_chars('lower-alpha');
- $canvas->register_string_subset($style->font_family, $chars);
- $chars = ListBullet::get_counter_chars('lower-greek');
- $canvas->register_string_subset($style->font_family, $chars);
-
- // the hex-decoded text of the content property, duplicated from AbstrctFrameReflower::_parse_string
- $decoded_string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/",
- function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); },
- $style->content);
- $chars = mb_strtoupper($style->content) . mb_strtolower($style->content) . mb_strtoupper($decoded_string) . mb_strtolower($decoded_string);
- $canvas->register_string_subset($style->font_family, $chars);
- continue;
- }
- }
- }
-
- $root = null;
-
- foreach ($this->tree->get_frames() as $frame) {
- // Set up the root frame
- if (is_null($root)) {
- $root = Factory::decorate_root($this->tree->get_root(), $this);
+ $root_frame = $this->tree->get_root();
+ $root = Factory::decorate_root($root_frame, $this);
+ foreach ($this->tree as $frame) {
+ if ($frame === $root_frame) {
continue;
}
-
- // Create the appropriate decorators, reflowers & positioners.
Factory::decorate_frame($frame, $this, $root);
}
@@ -820,11 +738,11 @@ class Dompdf
}
$metas = $this->dom->getElementsByTagName("meta");
- $labels = array(
+ $labels = [
"author" => "Author",
"keywords" => "Keywords",
"description" => "Subject",
- );
+ ];
/** @var \DOMElement $meta */
foreach ($metas as $meta) {
$name = mb_strtolower($meta->getAttribute("name"));
@@ -840,14 +758,24 @@ class Dompdf
}
}
- $root->set_containing_block(0, 0,$canvas->get_width(), $canvas->get_height());
+ $root->set_containing_block(0, 0, $canvas->get_width(), $canvas->get_height());
$root->set_renderer(new Renderer($this));
// This is where the magic happens:
$root->reflow();
+ if (isset($this->callbacks["end_document"])) {
+ $fs = $this->callbacks["end_document"];
+
+ foreach ($fs as $f) {
+ $canvas->page_script($f);
+ }
+ }
+
// Clean up cached images
- Cache::clear();
+ if (!$this->options->getDebugKeepTemp()) {
+ Cache::clear($this->options->getDebugPng());
+ }
global $_dompdf_warnings, $_dompdf_show_warnings;
if ($_dompdf_show_warnings && isset($_dompdf_warnings)) {
@@ -864,39 +792,24 @@ class Dompdf
}
if ($logOutputFile && is_writable($logOutputFile)) {
- $this->write_log();
+ $this->writeLog($logOutputFile, $startTime);
ob_end_clean();
}
- $this->restoreLocale();
- }
-
- /**
- * Add meta information to the PDF after rendering
- */
- public function add_info($label, $value)
- {
- $canvas = $this->getCanvas();
- if (!is_null($canvas)) {
- $canvas->add_info($label, $value);
- }
+ $this->restorePhpConfig();
}
/**
* Writes the output buffer in the log file
*
- * @return void
+ * @param string $logOutputFile
+ * @param float $startTime
*/
- private function write_log()
+ private function writeLog(string $logOutputFile, float $startTime): void
{
- $log_output_file = $this->getOptions()->getLogOutputFile();
- if (!$log_output_file || !is_writable($log_output_file)) {
- return;
- }
-
$frames = Frame::$ID_COUNTER;
$memory = memory_get_peak_usage(true) / 1024;
- $time = (microtime(true) - $this->startTime) * 1000;
+ $time = (microtime(true) - $startTime) * 1000;
$out = sprintf(
"%6d " .
@@ -909,7 +822,28 @@ class Dompdf
$out .= ob_get_contents();
ob_clean();
- file_put_contents($log_output_file, $out);
+ file_put_contents($logOutputFile, $out);
+ }
+
+ /**
+ * Add meta information to the PDF after rendering.
+ *
+ * @deprecated
+ */
+ public function add_info($label, $value)
+ {
+ $this->addInfo($label, $value);
+ }
+
+ /**
+ * Add meta information to the PDF after rendering.
+ *
+ * @param string $label Label of the value (Creator, Producer, etc.)
+ * @param string $value The text to set
+ */
+ public function addInfo(string $label, string $value): void
+ {
+ $this->canvas->add_info($label, $value);
}
/**
@@ -928,16 +862,13 @@ class Dompdf
* @param string $filename the name of the streamed file
* @param array $options header options (see above)
*/
- public function stream($filename = "document.pdf", $options = array())
+ public function stream($filename = "document.pdf", $options = [])
{
- $this->saveLocale();
+ $this->setPhpConfig();
- $canvas = $this->getCanvas();
- if (!is_null($canvas)) {
- $canvas->stream($filename, $options);
- }
+ $this->canvas->stream($filename, $options);
- $this->restoreLocale();
+ $this->restorePhpConfig();
}
/**
@@ -952,18 +883,13 @@ class Dompdf
*
* @return string|null
*/
- public function output($options = array())
+ public function output($options = [])
{
- $this->saveLocale();
+ $this->setPhpConfig();
- $canvas = $this->getCanvas();
- if (is_null($canvas)) {
- return null;
- }
+ $output = $this->canvas->output($options);
- $output = $canvas->output($options);
-
- $this->restoreLocale();
+ $this->restorePhpConfig();
return $output;
}
@@ -1035,7 +961,7 @@ class Dompdf
/**
* Sets the paper size & orientation
*
- * @param string|array $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}
+ * @param string|float[] $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}
* @param string $orientation 'portrait' or 'landscape'
* @return $this
*/
@@ -1049,19 +975,25 @@ class Dompdf
/**
* Gets the paper size
*
- * @param null|string|array $paperSize
- * @return int[] A four-element integer array
+ * @return float[] A four-element float array
*/
- public function getPaperSize($paperSize = null)
+ public function getPaperSize()
{
- $size = $paperSize !== null ? $paperSize : $this->paperSize;
- if (is_array($size)) {
- return $size;
- } else if (isset(Adapter\CPDF::$PAPER_SIZES[mb_strtolower($size)])) {
- return Adapter\CPDF::$PAPER_SIZES[mb_strtolower($size)];
+ $paper = $this->paperSize;
+ $orientation = $this->paperOrientation;
+
+ if (is_array($paper)) {
+ $size = array_map("floatval", $paper);
} else {
- return Adapter\CPDF::$PAPER_SIZES["letter"];
+ $paper = strtolower($paper);
+ $size = CPDF::$PAPER_SIZES[$paper] ?? CPDF::$PAPER_SIZES["letter"];
}
+
+ if (strtolower($orientation) === "landscape") {
+ [$size[2], $size[3]] = [$size[3], $size[2]];
+ }
+
+ return $size;
}
/**
@@ -1120,7 +1052,7 @@ class Dompdf
* @param string $protocol
* @return $this
*/
- public function setProtocol($protocol)
+ public function setProtocol(string $protocol)
{
$this->protocol = $protocol;
return $this;
@@ -1160,7 +1092,7 @@ class Dompdf
* @param string $baseHost
* @return $this
*/
- public function setBaseHost($baseHost)
+ public function setBaseHost(string $baseHost)
{
$this->baseHost = $baseHost;
return $this;
@@ -1202,7 +1134,7 @@ class Dompdf
* @param string $basePath
* @return $this
*/
- public function setBasePath($basePath)
+ public function setBasePath(string $basePath)
{
$this->basePath = $basePath;
return $this;
@@ -1265,12 +1197,12 @@ class Dompdf
/**
* Sets the HTTP context
*
- * @param resource $httpContext
+ * @param resource|array $httpContext
* @return $this
*/
public function setHttpContext($httpContext)
{
- $this->httpContext = $httpContext;
+ $this->options->setHttpContext($httpContext);
return $this;
}
@@ -1290,10 +1222,15 @@ class Dompdf
*/
public function getHttpContext()
{
- return $this->httpContext;
+ return $this->options->getHttpContext();
}
/**
+ * Set a custom `Canvas` instance to render the document to.
+ *
+ * Be aware that the instance will be replaced on render if the document
+ * defines a paper size different from the canvas.
+ *
* @param Canvas $canvas
* @return $this
*/
@@ -1384,8 +1321,13 @@ class Dompdf
*/
public function setOptions(Options $options)
{
+ // For backwards compatibility
+ if ($this->options && $this->options->getHttpContext() && !$options->getHttpContext()) {
+ $options->setHttpContext($this->options->getHttpContext());
+ }
+
$this->options = $options;
- $fontMetrics = $this->getFontMetrics();
+ $fontMetrics = $this->fontMetrics;
if (isset($fontMetrics)) {
$fontMetrics->setOptions($options);
}
@@ -1421,38 +1363,51 @@ class Dompdf
/**
* @param array $callbacks the set of callbacks to set
+ * @return $this
* @deprecated
*/
public function set_callbacks($callbacks)
{
- $this->setCallbacks($callbacks);
+ return $this->setCallbacks($callbacks);
}
/**
- * Sets callbacks for events like rendering of pages and elements.
- * The callbacks array contains arrays with 'event' set to 'begin_page',
- * 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or
- * object plus method to be called.
+ * Define callbacks that allow modifying the document during render.
*
- * The function 'f' must take an array as argument, which contains info
- * about the event.
+ * The callbacks array should contain arrays with `event` set to a callback
+ * event name and `f` set to a function or any other callable.
*
- * @param array $callbacks the set of callbacks to set
+ * The available callback events are:
+ * * `begin_page_reflow`: called before page reflow
+ * * `begin_frame`: called before a frame is rendered
+ * * `end_frame`: called after frame rendering is complete
+ * * `begin_page_render`: called before a page is rendered
+ * * `end_page_render`: called after page rendering is complete
+ * * `end_document`: called for every page after rendering is complete
+ *
+ * The function `f` receives three arguments `Frame $frame`, `Canvas $canvas`,
+ * and `FontMetrics $fontMetrics` for all events but `end_document`. For
+ * `end_document`, the function receives four arguments `int $pageNumber`,
+ * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics` instead.
+ *
+ * @param array $callbacks The set of callbacks to set.
+ * @return $this
*/
- public function setCallbacks($callbacks)
+ public function setCallbacks(array $callbacks): self
{
- if (is_array($callbacks)) {
- $this->callbacks = array();
- foreach ($callbacks as $c) {
- if (is_array($c) && isset($c['event']) && isset($c['f'])) {
- $event = $c['event'];
- $f = $c['f'];
- if (is_callable($f) && is_string($event)) {
- $this->callbacks[$event][] = $f;
- }
+ $this->callbacks = [];
+
+ foreach ($callbacks as $c) {
+ if (is_array($c) && isset($c["event"]) && isset($c["f"])) {
+ $event = $c["event"];
+ $f = $c["f"];
+ if (is_string($event) && is_callable($f)) {
+ $this->callbacks[$event][] = $f;
}
}
}
+
+ return $this;
}
/**
@@ -1505,12 +1460,11 @@ class Dompdf
*/
function __get($prop)
{
- switch ($prop)
- {
- case 'version' :
+ switch ($prop) {
+ case 'version':
return $this->version;
default:
- throw new Exception( 'Invalid property: ' . $prop );
+ throw new Exception('Invalid property: ' . $prop);
}
}
}
diff --git a/library/vendor/dompdf/src/Exception.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception.php
similarity index 84%
rename from library/vendor/dompdf/src/Exception.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception.php
index c9fb0df07..3a90e4771 100644
--- a/library/vendor/dompdf/src/Exception.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception.php
@@ -1,11 +1,9 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf;
/**
diff --git a/library/vendor/dompdf/src/Exception/ImageException.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception/ImageException.php
similarity index 84%
rename from library/vendor/dompdf/src/Exception/ImageException.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception/ImageException.php
index 62b44b1c8..ea1dfe407 100644
--- a/library/vendor/dompdf/src/Exception/ImageException.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception/ImageException.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\Exception;
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FontMetrics.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FontMetrics.php
new file mode 100644
index 000000000..5698c8823
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FontMetrics.php
@@ -0,0 +1,635 @@
+setCanvas($canvas);
+ $this->setOptions($options);
+ $this->loadFontFamilies();
+ }
+
+ /**
+ * @deprecated
+ */
+ public function save_font_families()
+ {
+ $this->saveFontFamilies();
+ }
+
+ /**
+ * Saves the stored font family cache
+ *
+ * The name and location of the cache file are determined by {@link
+ * FontMetrics::USER_FONTS_FILE}. This file should be writable by the
+ * webserver process.
+ *
+ * @see FontMetrics::loadFontFamilies()
+ */
+ public function saveFontFamilies()
+ {
+ file_put_contents($this->getUserFontsFilePath(), json_encode($this->userFonts, JSON_PRETTY_PRINT));
+ }
+
+ /**
+ * @deprecated
+ */
+ public function load_font_families()
+ {
+ $this->loadFontFamilies();
+ }
+
+ /**
+ * Loads the stored font family cache
+ *
+ * @see FontMetrics::saveFontFamilies()
+ */
+ public function loadFontFamilies()
+ {
+ $file = $this->options->getRootDir() . "/lib/fonts/installed-fonts.dist.json";
+ $this->bundledFonts = json_decode(file_get_contents($file), true);
+
+ if (is_readable($this->getUserFontsFilePath())) {
+ $this->userFonts = json_decode(file_get_contents($this->getUserFontsFilePath()), true);
+ } else {
+ $this->loadFontFamiliesLegacy();
+ }
+ }
+
+ private function loadFontFamiliesLegacy()
+ {
+ $legacyCacheFile = $this->options->getFontDir() . '/dompdf_font_family_cache.php';
+ if (is_readable($legacyCacheFile)) {
+ $fontDir = $this->options->getFontDir();
+ $rootDir = $this->options->getRootDir();
+
+ if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
+ if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
+
+ $cacheDataClosure = require $legacyCacheFile;
+ $cacheData = is_array($cacheDataClosure) ? $cacheDataClosure : $cacheDataClosure($fontDir, $rootDir);
+ if (is_array($cacheData)) {
+ foreach ($cacheData as $family => $variants) {
+ if (!isset($this->bundledFonts[$family]) && is_array($variants)) {
+ foreach ($variants as $variant => $variantPath) {
+ $variantName = basename($variantPath);
+ $variantDir = dirname($variantPath);
+ if ($variantDir == $fontDir) {
+ $this->userFonts[$family][$variant] = $variantName;
+ } else {
+ $this->userFonts[$family][$variant] = $variantPath;
+ }
+ }
+ }
+ }
+ $this->saveFontFamilies();
+ }
+ }
+ }
+
+ /**
+ * @param array $style
+ * @param string $remote_file
+ * @param resource $context
+ * @return bool
+ * @deprecated
+ */
+ public function register_font($style, $remote_file, $context = null)
+ {
+ return $this->registerFont($style, $remote_file);
+ }
+
+ /**
+ * @param array $style
+ * @param string $remoteFile
+ * @param resource $context
+ * @return bool
+ */
+ public function registerFont($style, $remoteFile, $context = null)
+ {
+ $fontname = mb_strtolower($style["family"]);
+ $families = $this->getFontFamilies();
+
+ $entry = [];
+ if (isset($families[$fontname])) {
+ $entry = $families[$fontname];
+ }
+
+ $styleString = $this->getType("{$style['weight']} {$style['style']}");
+
+ $remoteHash = md5($remoteFile);
+
+ $prefix = $fontname . "_" . $styleString;
+ $prefix = trim($prefix, "-");
+ if (function_exists('iconv')) {
+ $prefix = @iconv('utf-8', 'us-ascii//TRANSLIT', $prefix);
+ }
+ $prefix_encoding = mb_detect_encoding($prefix, mb_detect_order(), true);
+ $substchar = mb_substitute_character();
+ mb_substitute_character(0x005F);
+ $prefix = mb_convert_encoding($prefix, "ISO-8859-1", $prefix_encoding);
+ mb_substitute_character($substchar);
+ $prefix = preg_replace("[\W]", "_", $prefix);
+ $prefix = preg_replace("/[^-_\w]+/", "", $prefix);
+
+ $localFile = $prefix . "_" . $remoteHash;
+ $localFilePath = $this->getOptions()->getFontDir() . "/" . $localFile;
+
+ if (isset($entry[$styleString]) && $localFilePath == $entry[$styleString]) {
+ return true;
+ }
+
+
+ $entry[$styleString] = $localFile;
+
+ // Download the remote file
+ [$protocol] = Helpers::explode_url($remoteFile);
+ $allowed_protocols = $this->options->getAllowedProtocols();
+ if (!array_key_exists($protocol, $allowed_protocols)) {
+ Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The communication protocol is not supported.", __FILE__, __LINE__);
+ return false;
+ }
+
+ foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
+ [$result, $message] = $rule($remoteFile);
+ if ($result !== true) {
+ Helpers::record_warnings(E_USER_WARNING, "Error loading $remoteFile: $message", __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);
+ if ($remoteFileContent === null) {
+ return false;
+ }
+
+ $localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-");
+ file_put_contents($localTempFile, $remoteFileContent);
+
+ $font = Font::load($localTempFile);
+
+ if (!$font) {
+ unlink($localTempFile);
+ return false;
+ }
+
+ $font->parse();
+ $font->saveAdobeFontMetrics("$localFilePath.ufm");
+ $font->close();
+
+ unlink($localTempFile);
+
+ if ( !file_exists("$localFilePath.ufm") ) {
+ return false;
+ }
+
+ $fontExtension = ".ttf";
+ switch ($font->getFontType()) {
+ case "TrueType":
+ default:
+ $fontExtension = ".ttf";
+ break;
+ }
+
+ // Save the changes
+ file_put_contents($localFilePath.$fontExtension, $remoteFileContent);
+
+ if ( !file_exists($localFilePath.$fontExtension) ) {
+ unlink("$localFilePath.ufm");
+ return false;
+ }
+
+ $this->setFontFamily($fontname, $entry);
+
+ return true;
+ }
+
+ /**
+ * @param $text
+ * @param $font
+ * @param $size
+ * @param float $word_spacing
+ * @param float $char_spacing
+ * @return float
+ * @deprecated
+ */
+ public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
+ {
+ //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
+ return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing);
+ }
+
+ /**
+ * Calculates text size, in points
+ *
+ * @param string $text The text to be sized
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ * @param float $wordSpacing Word spacing, if any
+ * @param float $charSpacing Char spacing, if any
+ *
+ * @return float
+ */
+ public function getTextWidth(string $text, $font, float $size, float $wordSpacing = 0.0, float $charSpacing = 0.0): float
+ {
+ // @todo Make sure this cache is efficient before enabling it
+ static $cache = [];
+
+ if ($text === "") {
+ return 0;
+ }
+
+ // Don't cache long strings
+ $useCache = !isset($text[50]); // Faster than strlen
+
+ // Text-size calculations depend on the canvas used. Make sure to not
+ // return wrong values when switching canvas backends
+ $canvasClass = get_class($this->canvas);
+ $key = "$canvasClass/$font/$size/$wordSpacing/$charSpacing";
+
+ if ($useCache && isset($cache[$key][$text])) {
+ return $cache[$key][$text];
+ }
+
+ $width = $this->canvas->get_text_width($text, $font, $size, $wordSpacing, $charSpacing);
+
+ if ($useCache) {
+ $cache[$key][$text] = $width;
+ }
+
+ return $width;
+ }
+
+ /**
+ * @param $font
+ * @param $size
+ * @return float
+ * @deprecated
+ */
+ public function get_font_height($font, $size)
+ {
+ return $this->getFontHeight($font, $size);
+ }
+
+ /**
+ * Calculates font height, in points
+ *
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ *
+ * @return float
+ */
+ public function getFontHeight($font, float $size): float
+ {
+ return $this->canvas->get_font_height($font, $size);
+ }
+
+ /**
+ * Calculates font baseline, in points
+ *
+ * @param string $font The font file to use
+ * @param float $size The font size, in points
+ *
+ * @return float
+ */
+ public function getFontBaseline($font, float $size): float
+ {
+ return $this->canvas->get_font_baseline($font, $size);
+ }
+
+ /**
+ * @param $family_raw
+ * @param string $subtype_raw
+ * @return string
+ * @deprecated
+ */
+ public function get_font($family_raw, $subtype_raw = "normal")
+ {
+ return $this->getFont($family_raw, $subtype_raw);
+ }
+
+ /**
+ * Resolves a font family & subtype into an actual font file
+ * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
+ * the particular font family has no suitable font file, the default font
+ * ({@link Options::defaultFont}) is used. The font file returned
+ * is the absolute pathname to the font file on the system.
+ *
+ * @param string|null $familyRaw
+ * @param string $subtypeRaw
+ *
+ * @return string|null
+ */
+ public function getFont($familyRaw, $subtypeRaw = "normal")
+ {
+ static $cache = [];
+
+ if (isset($cache[$familyRaw][$subtypeRaw])) {
+ return $cache[$familyRaw][$subtypeRaw];
+ }
+
+ /* Allow calling for various fonts in search path. Therefore not immediately
+ * return replacement on non match.
+ * Only when called with NULL try replacement.
+ * When this is also missing there is really trouble.
+ * If only the subtype fails, nevertheless return failure.
+ * Only on checking the fallback font, check various subtypes on same font.
+ */
+
+ $subtype = strtolower($subtypeRaw);
+
+ $families = $this->getFontFamilies();
+ if ($familyRaw) {
+ $family = str_replace(["'", '"'], "", strtolower($familyRaw));
+
+ if (isset($families[$family][$subtype])) {
+ return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
+ }
+
+ return null;
+ }
+
+ $fallback_families = [strtolower($this->options->getDefaultFont()), "serif"];
+ foreach ($fallback_families as $family) {
+ if (isset($families[$family][$subtype])) {
+ return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
+ }
+
+ if (!isset($families[$family])) {
+ continue;
+ }
+
+ $family = $families[$family];
+
+ foreach ($family as $sub => $font) {
+ if (strpos($subtype, $sub) !== false) {
+ return $cache[$familyRaw][$subtypeRaw] = $font;
+ }
+ }
+
+ if ($subtype !== "normal") {
+ foreach ($family as $sub => $font) {
+ if ($sub !== "normal") {
+ return $cache[$familyRaw][$subtypeRaw] = $font;
+ }
+ }
+ }
+
+ $subtype = "normal";
+
+ if (isset($family[$subtype])) {
+ return $cache[$familyRaw][$subtypeRaw] = $family[$subtype];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $family
+ * @return null|string
+ * @deprecated
+ */
+ public function get_family($family)
+ {
+ return $this->getFamily($family);
+ }
+
+ /**
+ * @param string $family
+ * @return null|string
+ */
+ public function getFamily($family)
+ {
+ $family = str_replace(["'", '"'], "", mb_strtolower($family));
+ $families = $this->getFontFamilies();
+
+ if (isset($families[$family])) {
+ return $families[$family];
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $type
+ * @return string
+ * @deprecated
+ */
+ public function get_type($type)
+ {
+ return $this->getType($type);
+ }
+
+ /**
+ * @param string $type
+ * @return string
+ */
+ public function getType($type)
+ {
+ if (preg_match('/bold/i', $type)) {
+ $weight = 700;
+ } elseif (preg_match('/([1-9]00)/', $type, $match)) {
+ $weight = (int)$match[0];
+ } else {
+ $weight = 400;
+ }
+ $weight = $weight === 400 ? 'normal' : $weight;
+ $weight = $weight === 700 ? 'bold' : $weight;
+
+ $style = preg_match('/italic|oblique/i', $type) ? 'italic' : null;
+
+ if ($weight === 'normal' && $style !== null) {
+ return $style;
+ }
+
+ return $style === null
+ ? $weight
+ : $weight.'_'.$style;
+ }
+
+ /**
+ * @return array
+ * @deprecated
+ */
+ public function get_font_families()
+ {
+ return $this->getFontFamilies();
+ }
+
+ /**
+ * Returns the current font lookup table
+ *
+ * @return array
+ */
+ public function getFontFamilies()
+ {
+ if (!isset($this->fontFamilies)) {
+ $this->setFontFamilies();
+ }
+ return $this->fontFamilies;
+ }
+
+ /**
+ * Convert loaded fonts to font lookup table
+ *
+ * @return array
+ */
+ public function setFontFamilies()
+ {
+ $fontFamilies = [];
+ if (isset($this->bundledFonts) && is_array($this->bundledFonts)) {
+ foreach ($this->bundledFonts as $family => $variants) {
+ if (!isset($fontFamilies[$family])) {
+ $fontFamilies[$family] = array_map(function ($variant) {
+ return $this->getOptions()->getRootDir() . '/lib/fonts/' . $variant;
+ }, $variants);
+ }
+ }
+ }
+ if (isset($this->userFonts) && is_array($this->userFonts)) {
+ foreach ($this->userFonts as $family => $variants) {
+ $fontFamilies[$family] = array_map(function ($variant) {
+ $variantName = basename($variant);
+ if ($variantName === $variant) {
+ return $this->getOptions()->getFontDir() . '/' . $variant;
+ }
+ return $variant;
+ }, $variants);
+ }
+ }
+ $this->fontFamilies = $fontFamilies;
+ }
+
+ /**
+ * @param string $fontname
+ * @param mixed $entry
+ * @deprecated
+ */
+ public function set_font_family($fontname, $entry)
+ {
+ $this->setFontFamily($fontname, $entry);
+ }
+
+ /**
+ * @param string $fontname
+ * @param mixed $entry
+ */
+ public function setFontFamily($fontname, $entry)
+ {
+ $this->userFonts[mb_strtolower($fontname)] = $entry;
+ $this->saveFontFamilies();
+ unset($this->fontFamilies);
+ }
+
+ /**
+ * @return string
+ */
+ public function getUserFontsFilePath()
+ {
+ return $this->options->getFontDir() . '/' . self::USER_FONTS_FILE;
+ }
+
+ /**
+ * @param Options $options
+ * @return $this
+ */
+ public function setOptions(Options $options)
+ {
+ $this->options = $options;
+ unset($this->fontFamilies);
+ return $this;
+ }
+
+ /**
+ * @return Options
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * @param Canvas $canvas
+ * @return $this
+ */
+ public function setCanvas(Canvas $canvas)
+ {
+ $this->canvas = $canvas;
+ return $this;
+ }
+
+ /**
+ * @return Canvas
+ */
+ public function getCanvas()
+ {
+ return $this->canvas;
+ }
+}
diff --git a/library/vendor/dompdf/src/Frame.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame.php
similarity index 76%
rename from library/vendor/dompdf/src/Frame.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame.php
index 31ea00a1f..55136b22b 100644
--- a/library/vendor/dompdf/src/Frame.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame.php
@@ -1,16 +1,13 @@
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
+use Dompdf\Frame\FrameListIterator;
/**
* The main Frame class
@@ -39,12 +36,14 @@ class Frame
* Unique identifier for this frame. Used to reference this frame
* via the node.
*
- * @var string
+ * @var int
*/
protected $_id;
/**
* Unique id counter
+ *
+ * @var int
*/
public static $ID_COUNTER = 0; /*protected*/
@@ -55,14 +54,6 @@ class Frame
*/
protected $_style;
- /**
- * This frame's original style. Needed for cases where frames are
- * split across pages.
- *
- * @var Style
- */
- protected $_original_style;
-
/**
* This frame's parent in the document tree.
*
@@ -70,13 +61,6 @@ class Frame
*/
protected $_parent;
- /**
- * This frame's children
- *
- * @var Frame[]
- */
- protected $_frame_list;
-
/**
* This frame's first child. All children are handled as a
* doubly-linked list.
@@ -131,21 +115,21 @@ class Frame
/**
* This frame's decorator
*
- * @var \Dompdf\FrameDecorator\AbstractFrameDecorator
+ * @var FrameDecorator\AbstractFrameDecorator
*/
protected $_decorator;
/**
* This frame's containing line box
*
- * @var LineBox
+ * @var LineBox|null
*/
protected $_containing_line;
/**
* @var array
*/
- protected $_is_cache = array();
+ protected $_is_cache = [];
/**
* Tells whether the frame was already pushed to the next page
@@ -159,13 +143,6 @@ class Frame
*/
public $_float_next_line = false;
- /**
- * Tells whether the frame was split
- *
- * @var bool
- */
- public $_splitted;
-
/**
* @var int
*/
@@ -186,24 +163,23 @@ class Frame
$this->_prev_sibling = $this->_next_sibling = null;
$this->_style = null;
- $this->_original_style = null;
- $this->_containing_block = array(
+ $this->_containing_block = [
"x" => null,
"y" => null,
"w" => null,
"h" => null,
- );
+ ];
$this->_containing_block[0] =& $this->_containing_block["x"];
$this->_containing_block[1] =& $this->_containing_block["y"];
$this->_containing_block[2] =& $this->_containing_block["w"];
$this->_containing_block[3] =& $this->_containing_block["h"];
- $this->_position = array(
+ $this->_position = [
"x" => null,
"y" => null,
- );
+ ];
$this->_position[0] =& $this->_position["x"];
$this->_position[1] =& $this->_position["y"];
@@ -240,7 +216,7 @@ class Frame
{
$whitespace = $this->get_style()->white_space;
- return in_array($whitespace, array("pre", "pre-wrap", "pre-line"));
+ return in_array($whitespace, ["pre", "pre-wrap", "pre-line"]);
}
/**
@@ -299,14 +275,8 @@ class Frame
$this->_parent->get_node()->removeChild($this->_node);
}
- $this->_style->dispose();
$this->_style = null;
unset($this->_style);
-
- $this->_original_style->dispose();
- $this->_original_style = null;
- unset($this->_original_style);
-
}
/**
@@ -322,17 +292,7 @@ class Frame
$this->_containing_block["w"] = null;
$this->_containing_block["h"] = null;
- $this->_style = null;
- unset($this->_style);
- $this->_style = clone $this->_original_style;
-
- // If this represents a generated node then child nodes represent generated content.
- // Remove the children since the content will be generated next time this frame is reflowed.
- if ($this->_node->nodeName === "dompdf_generated" && $this->_style->content != "normal") {
- foreach ($this->get_children() as $child) {
- $this->remove_child($child);
- }
- }
+ $this->_style->reset();
}
/**
@@ -344,7 +304,7 @@ class Frame
}
/**
- * @return string
+ * @return int
*/
public function get_id()
{
@@ -360,11 +320,12 @@ class Frame
}
/**
+ * @deprecated
* @return Style
*/
public function get_original_style()
{
- return $this->_original_style;
+ return $this->_style;
}
/**
@@ -376,7 +337,7 @@ class Frame
}
/**
- * @return \Dompdf\FrameDecorator\AbstractFrameDecorator
+ * @return FrameDecorator\AbstractFrameDecorator
*/
public function get_decorator()
{
@@ -416,17 +377,11 @@ class Frame
}
/**
- * @return FrameList|Frame[]
+ * @return FrameListIterator
*/
- public function get_children()
+ public function get_children(): FrameListIterator
{
- if (isset($this->_frame_list)) {
- return $this->_frame_list;
- }
-
- $this->_frame_list = new FrameList($this);
-
- return $this->_frame_list;
+ return new FrameListIterator($this);
}
// Layout property accessors
@@ -434,7 +389,7 @@ class Frame
/**
* Containing block dimensions
*
- * @param $i string The key of the wanted containing block's dimension (x, y, w, h)
+ * @param string|null $i The key of the wanted containing block's dimension (x, y, w, h)
*
* @return float[]|float
*/
@@ -450,9 +405,9 @@ class Frame
/**
* Block position
*
- * @param $i string The key of the wanted position value (x, y)
+ * @param string|null $i The key of the wanted position value (x, y)
*
- * @return array|float
+ * @return float[]|float
*/
public function get_position($i = null)
{
@@ -465,38 +420,17 @@ class Frame
//........................................................................
- /**
- * Return the height of the margin box of the frame, in pt. Meaningless
- * unless the height has been calculated properly.
- *
- * @return float
- */
- public function get_margin_height()
- {
- $style = $this->_style;
-
- return (float)$style->length_in_pt(array(
- $style->height,
- $style->margin_top,
- $style->margin_bottom,
- $style->border_top_width,
- $style->border_bottom_width,
- $style->padding_top,
- $style->padding_bottom
- ), $this->_containing_block["h"]);
- }
-
/**
* Return the width of the margin box of the frame, in pt. Meaningless
* unless the width has been calculated properly.
*
* @return float
*/
- public function get_margin_width()
+ public function get_margin_width(): float
{
$style = $this->_style;
- return (float)$style->length_in_pt(array(
+ return (float)$style->length_in_pt([
$style->width,
$style->margin_left,
$style->margin_right,
@@ -504,135 +438,185 @@ class Frame
$style->border_right_width,
$style->padding_left,
$style->padding_right
- ), $this->_containing_block["w"]);
+ ], $this->_containing_block["w"]);
}
/**
+ * Return the height of the margin box of the frame, in pt. Meaningless
+ * unless the height has been calculated properly.
+ *
* @return float
*/
- public function get_break_margins()
+ public function get_margin_height(): float
{
$style = $this->_style;
- return (float)$style->length_in_pt(array(
- //$style->height,
- $style->margin_top,
- $style->margin_bottom,
- $style->border_top_width,
- $style->border_bottom_width,
- $style->padding_top,
- $style->padding_bottom
- ), $this->_containing_block["h"]);
- }
-
- /**
- * Return the content box (x,y,w,h) of the frame
- *
- * @return array
- */
- public function get_content_box()
- {
- $style = $this->_style;
- $cb = $this->_containing_block;
-
- $x = $this->_position["x"] +
- (float)$style->length_in_pt(array($style->margin_left,
- $style->border_left_width,
- $style->padding_left),
- $cb["w"]);
-
- $y = $this->_position["y"] +
- (float)$style->length_in_pt(array($style->margin_top,
- $style->border_top_width,
- $style->padding_top),
- $cb["h"]);
-
- $w = $style->length_in_pt($style->width, $cb["w"]);
-
- $h = $style->length_in_pt($style->height, $cb["h"]);
-
- return array(0 => $x, "x" => $x,
- 1 => $y, "y" => $y,
- 2 => $w, "w" => $w,
- 3 => $h, "h" => $h);
- }
-
- /**
- * Return the padding box (x,y,w,h) of the frame
- *
- * @return array
- */
- public function get_padding_box()
- {
- $style = $this->_style;
- $cb = $this->_containing_block;
-
- $x = $this->_position["x"] +
- (float)$style->length_in_pt(array($style->margin_left,
- $style->border_left_width),
- $cb["w"]);
-
- $y = $this->_position["y"] +
- (float)$style->length_in_pt(array($style->margin_top,
- $style->border_top_width),
- $cb["h"]);
-
- $w = $style->length_in_pt(array($style->padding_left,
- $style->width,
- $style->padding_right),
- $cb["w"]);
-
- $h = $style->length_in_pt(array($style->padding_top,
+ return (float)$style->length_in_pt(
+ [
$style->height,
- $style->padding_bottom),
- $cb["h"]);
-
- return array(0 => $x, "x" => $x,
- 1 => $y, "y" => $y,
- 2 => $w, "w" => $w,
- 3 => $h, "h" => $h);
+ (float)$style->length_in_pt(
+ [
+ $style->border_top_width,
+ $style->border_bottom_width,
+ $style->margin_top,
+ $style->margin_bottom,
+ $style->padding_top,
+ $style->padding_bottom
+ ], $this->_containing_block["w"]
+ )
+ ],
+ $this->_containing_block["h"]
+ );
}
/**
- * Return the border box of the frame
+ * Return the content box (x,y,w,h) of the frame.
*
- * @return array
+ * Width and height might be reported as 0 if they have not been resolved
+ * yet.
+ *
+ * @return float[]
*/
- public function get_border_box()
+ public function get_content_box(): array
+ {
+ $style = $this->_style;
+ $cb = $this->_containing_block;
+
+ $x = $this->_position["x"] +
+ (float)$style->length_in_pt(
+ [
+ $style->margin_left,
+ $style->border_left_width,
+ $style->padding_left
+ ],
+ $cb["w"]
+ );
+
+ $y = $this->_position["y"] +
+ (float)$style->length_in_pt(
+ [
+ $style->margin_top,
+ $style->border_top_width,
+ $style->padding_top
+ ], $cb["w"]
+ );
+
+ $w = (float)$style->length_in_pt($style->width, $cb["w"]);
+
+ $h = (float)$style->length_in_pt($style->height, $cb["h"]);
+
+ return [0 => $x, "x" => $x,
+ 1 => $y, "y" => $y,
+ 2 => $w, "w" => $w,
+ 3 => $h, "h" => $h];
+ }
+
+ /**
+ * Return the padding box (x,y,w,h) of the frame.
+ *
+ * Width and height might be reported as 0 if they have not been resolved
+ * yet.
+ *
+ * @return float[]
+ */
+ public function get_padding_box(): array
+ {
+ $style = $this->_style;
+ $cb = $this->_containing_block;
+
+ $x = $this->_position["x"] +
+ (float)$style->length_in_pt(
+ [
+ $style->margin_left,
+ $style->border_left_width
+ ],
+ $cb["w"]
+ );
+
+ $y = $this->_position["y"] +
+ (float)$style->length_in_pt(
+ [
+ $style->margin_top,
+ $style->border_top_width
+ ],
+ $cb["h"]
+ );
+
+ $w = (float)$style->length_in_pt(
+ [
+ $style->padding_left,
+ $style->width,
+ $style->padding_right
+ ],
+ $cb["w"]
+ );
+
+ $h = (float)$style->length_in_pt(
+ [
+ $style->padding_top,
+ $style->padding_bottom,
+ $style->length_in_pt($style->height, $cb["h"])
+ ],
+ $cb["w"]
+ );
+
+ return [0 => $x, "x" => $x,
+ 1 => $y, "y" => $y,
+ 2 => $w, "w" => $w,
+ 3 => $h, "h" => $h];
+ }
+
+ /**
+ * Return the border box of the frame.
+ *
+ * Width and height might be reported as 0 if they have not been resolved
+ * yet.
+ *
+ * @return float[]
+ */
+ public function get_border_box(): array
{
$style = $this->_style;
$cb = $this->_containing_block;
$x = $this->_position["x"] + (float)$style->length_in_pt($style->margin_left, $cb["w"]);
- $y = $this->_position["y"] + (float)$style->length_in_pt($style->margin_top, $cb["h"]);
+ $y = $this->_position["y"] + (float)$style->length_in_pt($style->margin_top, $cb["w"]);
- $w = $style->length_in_pt(array($style->border_left_width,
+ $w = (float)$style->length_in_pt(
+ [
+ $style->border_left_width,
$style->padding_left,
$style->width,
$style->padding_right,
- $style->border_right_width),
- $cb["w"]);
+ $style->border_right_width
+ ],
+ $cb["w"]
+ );
- $h = $style->length_in_pt(array($style->border_top_width,
+ $h = (float)$style->length_in_pt(
+ [
+ $style->border_top_width,
$style->padding_top,
- $style->height,
$style->padding_bottom,
- $style->border_bottom_width),
- $cb["h"]);
+ $style->border_bottom_width,
+ $style->length_in_pt($style->height, $cb["h"])
+ ],
+ $cb["w"]
+ );
- return array(0 => $x, "x" => $x,
+ return [0 => $x, "x" => $x,
1 => $y, "y" => $y,
2 => $w, "w" => $w,
- 3 => $h, "h" => $h);
+ 3 => $h, "h" => $h];
}
/**
- * @param null $opacity
+ * @param float|null $opacity
*
* @return float
*/
- public function get_opacity($opacity = null)
+ public function get_opacity(?float $opacity = null): float
{
if ($opacity !== null) {
$this->set_opacity($opacity);
@@ -642,7 +626,7 @@ class Frame
}
/**
- * @return LineBox
+ * @return LineBox|null
*/
public function &get_containing_line()
{
@@ -650,10 +634,10 @@ class Frame
}
//........................................................................
-
// Set methods
+
/**
- * @param $id
+ * @param int $id
*/
public function set_id($id)
{
@@ -670,18 +654,14 @@ class Frame
/**
* @param Style $style
*/
- public function set_style(Style $style)
+ public function set_style(Style $style): void
{
- if (is_null($this->_style)) {
- $this->_original_style = clone $style;
- }
-
- //$style->set_frame($this);
+ // $style->set_frame($this);
$this->_style = $style;
}
/**
- * @param \Dompdf\FrameDecorator\AbstractFrameDecorator $decorator
+ * @param FrameDecorator\AbstractFrameDecorator $decorator
*/
public function set_decorator(FrameDecorator\AbstractFrameDecorator $decorator)
{
@@ -689,10 +669,10 @@ class Frame
}
/**
- * @param null $x
- * @param null $y
- * @param null $w
- * @param null $h
+ * @param float|float[]|null $x
+ * @param float|null $y
+ * @param float|null $w
+ * @param float|null $h
*/
public function set_containing_block($x = null, $y = null, $w = null, $h = null)
{
@@ -720,13 +700,13 @@ class Frame
}
/**
- * @param null $x
- * @param null $y
+ * @param float|float[]|null $x
+ * @param float|null $y
*/
public function set_position($x = null, $y = null)
{
if (is_array($x)) {
- list($x, $y) = array($x["x"], $x["y"]);
+ list($x, $y) = [$x["x"], $x["y"]];
}
if (is_numeric($x)) {
@@ -739,12 +719,12 @@ class Frame
}
/**
- * @param $opacity
+ * @param float $opacity
*/
- public function set_opacity($opacity)
+ public function set_opacity(float $opacity): void
{
$parent = $this->get_parent();
- $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0);
+ $base_opacity = $parent && $parent->_opacity !== null ? $parent->_opacity : 1.0;
$this->_opacity = $base_opacity * $opacity;
}
@@ -767,7 +747,7 @@ class Frame
return in_array(
"auto",
- array(
+ [
$style->height,
$style->margin_top,
$style->margin_bottom,
@@ -776,7 +756,7 @@ class Frame
$style->padding_top,
$style->padding_bottom,
$this->_containing_block["h"]
- ),
+ ],
true
);
}
@@ -792,7 +772,7 @@ class Frame
return in_array(
"auto",
- array(
+ [
$style->width,
$style->margin_left,
$style->margin_right,
@@ -801,7 +781,7 @@ class Frame
$style->padding_left,
$style->padding_right,
$this->_containing_block["w"]
- ),
+ ],
true
);
}
@@ -811,7 +791,7 @@ class Frame
*
* @return bool
*/
- public function is_text_node()
+ public function is_text_node(): bool
{
if (isset($this->_is_cache["text_node"])) {
return $this->_is_cache["text_node"];
@@ -823,70 +803,91 @@ class Frame
/**
* @return bool
*/
- public function is_positionned()
+ public function is_positioned(): bool
{
- if (isset($this->_is_cache["positionned"])) {
- return $this->_is_cache["positionned"];
+ if (isset($this->_is_cache["positioned"])) {
+ return $this->_is_cache["positioned"];
}
$position = $this->get_style()->position;
- return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES);
+ return $this->_is_cache["positioned"] = in_array($position, Style::POSITIONED_TYPES, true);
}
/**
* @return bool
*/
- public function is_absolute()
+ public function is_absolute(): bool
{
if (isset($this->_is_cache["absolute"])) {
return $this->_is_cache["absolute"];
}
- $position = $this->get_style()->position;
-
- return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed");
+ return $this->_is_cache["absolute"] = $this->get_style()->is_absolute();
}
/**
+ * Whether the frame is a block container.
+ *
* @return bool
*/
- public function is_block()
+ public function is_block(): bool
{
if (isset($this->_is_cache["block"])) {
return $this->_is_cache["block"];
}
- return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES);
+ return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::BLOCK_TYPES, true);
}
/**
+ * Whether the frame has a block-level display type.
+ *
* @return bool
*/
- public function is_inline_block()
+ public function is_block_level(): bool
{
- if (isset($this->_is_cache["inline_block"])) {
- return $this->_is_cache["inline_block"];
+ if (isset($this->_is_cache["block_level"])) {
+ return $this->_is_cache["block_level"];
}
- return $this->_is_cache["inline_block"] = ($this->get_style()->display === 'inline-block');
+ $display = $this->get_style()->display;
+
+ return $this->_is_cache["block_level"] = in_array($display, Style::BLOCK_LEVEL_TYPES, true);
+ }
+
+ /**
+ * Whether the frame has an inline-level display type.
+ *
+ * @return bool
+ */
+ public function is_inline_level(): bool
+ {
+ if (isset($this->_is_cache["inline_level"])) {
+ return $this->_is_cache["inline_level"];
+ }
+
+ $display = $this->get_style()->display;
+
+ return $this->_is_cache["inline_level"] = in_array($display, Style::INLINE_LEVEL_TYPES, true);
}
/**
* @return bool
*/
- public function is_in_flow()
+ public function is_in_flow(): bool
{
if (isset($this->_is_cache["in_flow"])) {
return $this->_is_cache["in_flow"];
}
- return $this->_is_cache["in_flow"] = !($this->get_style()->float !== "none" || $this->is_absolute());
+
+ return $this->_is_cache["in_flow"] = $this->get_style()->is_in_flow();
}
/**
* @return bool
*/
- public function is_pre()
+ public function is_pre(): bool
{
if (isset($this->_is_cache["pre"])) {
return $this->_is_cache["pre"];
@@ -894,13 +895,13 @@ class Frame
$white_space = $this->get_style()->white_space;
- return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap"));
+ return $this->_is_cache["pre"] = in_array($white_space, ["pre", "pre-wrap"], true);
}
/**
* @return bool
*/
- public function is_table()
+ public function is_table(): bool
{
if (isset($this->_is_cache["table"])) {
return $this->_is_cache["table"];
@@ -908,15 +909,15 @@ class Frame
$display = $this->get_style()->display;
- return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES);
+ return $this->_is_cache["table"] = in_array($display, Style::TABLE_TYPES, true);
}
/**
* Inserts a new child at the beginning of the Frame
*
- * @param $child Frame The new Frame to insert
- * @param $update_node boolean Whether or not to update the DOM
+ * @param Frame $child The new Frame to insert
+ * @param bool $update_node Whether or not to update the DOM
*/
public function prepend_child(Frame $child, $update_node = true)
{
@@ -947,8 +948,8 @@ class Frame
/**
* Inserts a new child at the end of the Frame
*
- * @param $child Frame The new Frame to insert
- * @param $update_node boolean Whether or not to update the DOM
+ * @param Frame $child The new Frame to insert
+ * @param bool $update_node Whether or not to update the DOM
*/
public function append_child(Frame $child, $update_node = true)
{
@@ -984,9 +985,9 @@ class Frame
/**
* Inserts a new child immediately before the specified frame
*
- * @param $new_child Frame The new Frame to insert
- * @param $ref Frame The Frame after the new Frame
- * @param $update_node boolean Whether or not to update the DOM
+ * @param Frame $new_child The new Frame to insert
+ * @param Frame $ref The Frame after the new Frame
+ * @param bool $update_node Whether or not to update the DOM
*
* @throws Exception
*/
@@ -1032,9 +1033,9 @@ class Frame
/**
* Inserts a new child immediately after the specified frame
*
- * @param $new_child Frame The new Frame to insert
- * @param $ref Frame The Frame before the new Frame
- * @param $update_node boolean Whether or not to update the DOM
+ * @param Frame $new_child The new Frame to insert
+ * @param Frame $ref The Frame before the new Frame
+ * @param bool $update_node Whether or not to update the DOM
*
* @throws Exception
*/
@@ -1086,7 +1087,7 @@ class Frame
* Remove a child frame
*
* @param Frame $child
- * @param boolean $update_node Whether or not to remove the DOM node
+ * @param bool $update_node Whether or not to remove the DOM node
*
* @throws Exception
* @return Frame The removed child frame
@@ -1206,11 +1207,11 @@ class Frame
$str .= "\n";
if (php_sapi_name() === "cli") {
- $str = strip_tags(str_replace(array(" ", "", " "),
- array("\n", "", ""),
+ $str = strip_tags(str_replace([" ", "", " "],
+ ["\n", "", ""],
$str));
}
return $str;
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/src/Frame/Factory.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/Factory.php
similarity index 85%
rename from library/vendor/dompdf/src/Frame/Factory.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/Factory.php
index e14f75b62..b4bab8871 100644
--- a/library/vendor/dompdf/src/Frame/Factory.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/Factory.php
@@ -1,13 +1,11 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\Frame;
-use Dompdf\Css\Style;
use Dompdf\Dompdf;
use Dompdf\Exception;
use Dompdf\Frame;
@@ -25,7 +23,6 @@ use Dompdf\Positioner\AbstractPositioner;
* objects. This is determined primarily by the Frame's display type, but
* also by the Frame's node's type (e.g. DomElement vs. #text)
*
- * @access private
* @package dompdf
*/
class Factory
@@ -60,7 +57,7 @@ class Factory
*
* @param Frame $frame The frame to decorate
* @param Dompdf $dompdf The dompdf instance
- * @param Frame $root The frame to decorate
+ * @param Frame $root The root of the frame
*
* @throws Exception
* @return AbstractFrameDecorator
@@ -68,31 +65,17 @@ class Factory
*/
static function decorate_frame(Frame $frame, Dompdf $dompdf, Frame $root = null)
{
- if (is_null($dompdf)) {
- throw new Exception("The DOMPDF argument is required");
- }
-
$style = $frame->get_style();
-
- // Floating (and more generally out-of-flow) elements are blocks
- // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/
- if (!$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) {
- $style->display = "block";
- }
-
$display = $style->display;
switch ($display) {
- case "flex": //FIXME: display type not yet supported
- case "table-caption": //FIXME: display type not yet supported
case "block":
$positioner = "Block";
$decorator = "Block";
$reflower = "Block";
break;
- case "inline-flex": //FIXME: display type not yet supported
case "inline-block":
$positioner = "Inline";
$decorator = "Block";
@@ -105,13 +88,8 @@ class Factory
$decorator = "Text";
$reflower = "Text";
} else {
- if ($style->float !== "none") {
- $decorator = "Block";
- $reflower = "Block";
- } else {
- $decorator = "Inline";
- $reflower = "Inline";
- }
+ $decorator = "Inline";
+ $reflower = "Inline";
}
break;
@@ -182,7 +160,6 @@ class Factory
break;
default:
- // FIXME: should throw some sort of warning or something?
case "none":
if ($style->_dompdf_keep !== "yes") {
// Remove the node and the frame
@@ -211,7 +188,7 @@ class Factory
// Handle nodeName
if ($node->nodeName === "img") {
- $style->display = "-dompdf-image";
+ $style->set_prop("display", "-dompdf-image");
$decorator = "Image";
$reflower = "Image";
}
@@ -237,8 +214,7 @@ class Factory
$node = $frame->get_node();
$parent_node = $node->parentNode;
-
- if ($parent_node) {
+ if ($parent_node && $parent_node instanceof \DOMElement) {
if (!$parent_node->hasAttribute("dompdf-children-count")) {
$xpath = new DOMXPath($xml);
$count = $xpath->query("li", $parent_node)->length;
@@ -260,7 +236,7 @@ class Factory
}
$new_style = $dompdf->getCss()->create_style();
- $new_style->display = "-dompdf-list-bullet";
+ $new_style->set_prop("display", "-dompdf-list-bullet");
$new_style->inherit($style);
$b_f->set_style($new_style);
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameListIterator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameListIterator.php
new file mode 100644
index 000000000..01575505a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameListIterator.php
@@ -0,0 +1,100 @@
+parent = $frame;
+ $this->rewind();
+ }
+
+ public function rewind(): void
+ {
+ $this->cur = $this->parent->get_first_child();
+ $this->prev = null;
+ $this->num = 0;
+ }
+
+ /**
+ * @return bool
+ */
+ public function valid(): bool
+ {
+ return $this->cur !== null;
+ }
+
+ /**
+ * @return int
+ */
+ public function key(): int
+ {
+ return $this->num;
+ }
+
+ /**
+ * @return Frame|null
+ */
+ public function current(): ?Frame
+ {
+ return $this->cur;
+ }
+
+ public function next(): void
+ {
+ if ($this->cur === null) {
+ return;
+ }
+
+ if ($this->cur->get_parent() === $this->parent) {
+ $this->prev = $this->cur;
+ $this->cur = $this->cur->get_next_sibling();
+ $this->num++;
+ } else {
+ // Continue from the previous child if the current frame has been
+ // moved to another parent
+ $this->cur = $this->prev !== null
+ ? $this->prev->get_next_sibling()
+ : $this->parent->get_first_child();
+ }
+ }
+}
diff --git a/library/vendor/dompdf/src/Frame/FrameTree.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTree.php
similarity index 92%
rename from library/vendor/dompdf/src/Frame/FrameTree.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTree.php
index 1d2585439..6d012d8d3 100644
--- a/library/vendor/dompdf/src/Frame/FrameTree.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTree.php
@@ -1,5 +1,9 @@
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
+use IteratorAggregate;
/**
* Represents an entire document as a tree of frames
@@ -27,14 +25,14 @@ use Dompdf\Frame;
*
* @package dompdf
*/
-class FrameTree
+class FrameTree implements IteratorAggregate
{
/**
* Tags to ignore while parsing the tree
*
* @var array
*/
- protected static $HIDDEN_TAGS = array(
+ protected static $HIDDEN_TAGS = [
"area",
"base",
"basefont",
@@ -46,7 +44,7 @@ class FrameTree
"noembed",
"param",
"#comment"
- );
+ ];
/**
* The main DomDocument
@@ -86,7 +84,7 @@ class FrameTree
{
$this->_dom = $dom;
$this->_root = null;
- $this->_registry = array();
+ $this->_registry = [];
}
/**
@@ -124,11 +122,22 @@ class FrameTree
/**
* Returns a post-order iterator for all frames in the tree
*
- * @return FrameTreeList|Frame[]
+ * @deprecated Iterate the tree directly instead
+ * @return FrameTreeIterator
*/
- public function get_frames()
+ public function get_frames(): FrameTreeIterator
{
- return new FrameTreeList($this->_root);
+ return new FrameTreeIterator($this->_root);
+ }
+
+ /**
+ * Returns a post-order iterator for all frames in the tree
+ *
+ * @return FrameTreeIterator
+ */
+ public function getIterator(): FrameTreeIterator
+ {
+ return new FrameTreeIterator($this->_root);
}
/**
@@ -239,7 +248,7 @@ class FrameTree
}
// Store the children in an array so that the tree can be modified
- $children = array();
+ $children = [];
$length = $node->childNodes->length;
for ($i = 0; $i < $length; $i++) {
$children[] = $node->childNodes->item($i);
@@ -312,4 +321,4 @@ class FrameTree
return $frame_id;
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/src/Frame/FrameTreeIterator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTreeIterator.php
similarity index 70%
rename from library/vendor/dompdf/src/Frame/FrameTreeIterator.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTreeIterator.php
index ebca7e99f..4da8da1ee 100644
--- a/library/vendor/dompdf/src/Frame/FrameTreeIterator.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTreeIterator.php
@@ -1,4 +1,9 @@
_num = 0;
}
- /**
- *
- */
- public function rewind()
+ public function rewind(): void
{
- $this->_stack = array($this->_root);
+ $this->_stack = [$this->_root];
$this->_num = 0;
}
/**
* @return bool
*/
- public function valid()
+ public function valid(): bool
{
return count($this->_stack) > 0;
}
@@ -58,7 +59,7 @@ class FrameTreeIterator implements Iterator
/**
* @return int
*/
- public function key()
+ public function key(): int
{
return $this->_num;
}
@@ -66,20 +67,14 @@ class FrameTreeIterator implements Iterator
/**
* @return Frame
*/
- public function current()
+ public function current(): Frame
{
return end($this->_stack);
}
- /**
- * @return Frame
- */
- public function next()
+ public function next(): void
{
- $b = end($this->_stack);
-
- // Pop last element
- unset($this->_stack[key($this->_stack)]);
+ $b = array_pop($this->_stack);
$this->_num++;
// Push all children onto the stack in reverse order
@@ -89,8 +84,5 @@ class FrameTreeIterator implements Iterator
$this->_stack[] = $c;
}
}
-
- return $b;
}
}
-
diff --git a/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
similarity index 65%
rename from library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
index 777fc3ce8..2c1fcc5df 100644
--- a/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
@@ -1,26 +1,23 @@
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
/**
* Base AbstractFrameDecorator class
@@ -31,7 +28,12 @@ abstract class AbstractFrameDecorator extends Frame
{
const DEFAULT_COUNTER = "-dompdf-default-counter";
- public $_counters = array(); // array([id] => counter_value) (for generated content)
+ /**
+ * array([id] => counter_value) (for generated content)
+ *
+ * @var array
+ */
+ public $_counters = [];
/**
* The root node of the DOM tree
@@ -57,7 +59,7 @@ abstract class AbstractFrameDecorator extends Frame
/**
* Reflower object used to calculate frame dimensions (Strategy pattern)
*
- * @var \Dompdf\FrameReflower\AbstractFrameReflower
+ * @var AbstractFrameReflower
*/
protected $_reflower;
@@ -76,11 +78,11 @@ abstract class AbstractFrameDecorator extends Frame
private $_block_parent;
/**
- * First positionned parent (position: relative | absolute | fixed)
+ * First positioned parent (position: relative | absolute | fixed)
*
* @var AbstractFrameDecorator
*/
- private $_positionned_parent;
+ private $_positioned_parent;
/**
* Cache for the get_parent while loop results
@@ -89,6 +91,27 @@ abstract class AbstractFrameDecorator extends Frame
*/
private $_cached_parent;
+ /**
+ * Whether generated content and counters have been set.
+ *
+ * @var bool
+ */
+ public $content_set = false;
+
+ /**
+ * Whether the frame has been split
+ *
+ * @var bool
+ */
+ public $is_split = false;
+
+ /**
+ * Whether the frame is a split-off frame
+ *
+ * @var bool
+ */
+ public $is_split_off = false;
+
/**
* Class constructor
*
@@ -104,7 +127,7 @@ abstract class AbstractFrameDecorator extends Frame
}
/**
- * "Destructor": foribly free all references held by this object
+ * "Destructor": forcibly free all references held by this object
*
* @param bool $recursive if true, call dispose on all children
*/
@@ -135,12 +158,20 @@ abstract class AbstractFrameDecorator extends Frame
*
* @param DOMNode $node
*
- * @return Frame
+ * @return AbstractFrameDecorator
*/
function copy(DOMNode $node)
{
$frame = new Frame($node);
- $frame->set_style(clone $this->_frame->get_original_style());
+ $style = clone $this->_frame->get_style();
+
+ $style->reset();
+ $frame->set_style($style);
+
+ if ($node instanceof DOMElement && $node->hasAttribute("id")) {
+ $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
+ $node->removeAttribute("id");
+ }
return Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
}
@@ -148,20 +179,22 @@ abstract class AbstractFrameDecorator extends Frame
/**
* Create a deep copy: copy this node and all children
*
- * @return Frame
+ * @return AbstractFrameDecorator
*/
function deep_copy()
{
- $node = $this->_frame->get_node();
+ $node = $this->_frame->get_node()->cloneNode();
+ $frame = new Frame($node);
+ $style = clone $this->_frame->get_style();
+
+ $style->reset();
+ $frame->set_style($style);
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
$node->removeAttribute("id");
}
- $frame = new Frame($node->cloneNode());
- $frame->set_style(clone $this->_frame->get_original_style());
-
$deco = Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
foreach ($this->get_children() as $child) {
@@ -172,15 +205,41 @@ abstract class AbstractFrameDecorator extends Frame
}
/**
- * Delegate calls to decorated frame object
+ * Create an anonymous child frame, inheriting styles from this frame.
+ *
+ * @param string $node_name
+ * @param string $display
+ *
+ * @return AbstractFrameDecorator
*/
+ public function create_anonymous_child(string $node_name, string $display): AbstractFrameDecorator
+ {
+ $style = $this->get_style();
+ $child_style = $style->get_stylesheet()->create_style();
+ $child_style->set_prop("display", $display);
+ $child_style->inherit($style);
+
+ $node = $this->get_node()->ownerDocument->createElement($node_name);
+ $frame = new Frame($node);
+ $frame->set_style($child_style);
+
+ return Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
+ }
+
function reset()
{
$this->_frame->reset();
+ $this->_reflower->reset();
+ $this->reset_generated_content();
+ $this->revert_counter_increment();
- $this->_counters = array();
+ $this->content_set = false;
+ $this->_counters = [];
- $this->_cached_parent = null; //clear get_parent() cache
+ // clear parent lookup caches
+ $this->_cached_parent = null;
+ $this->_block_parent = null;
+ $this->_positioned_parent = null;
// Reset all children
foreach ($this->get_children() as $child) {
@@ -188,11 +247,42 @@ abstract class AbstractFrameDecorator extends Frame
}
}
- // Getters -----------
+ /**
+ * If this represents a generated node then child nodes represent generated
+ * content. Remove the children since the content will be generated next
+ * time this frame is reflowed.
+ */
+ protected function reset_generated_content(): void
+ {
+ if ($this->content_set
+ && $this->get_node()->nodeName === "dompdf_generated"
+ ) {
+ $content = $this->get_style()->content;
+
+ if ($content !== "normal" && $content !== "none") {
+ foreach ($this->get_children() as $child) {
+ $this->remove_child($child);
+ }
+ }
+ }
+ }
/**
- * @return string
+ * Decrement any counters that were incremented on the current node, unless
+ * that node is the body.
*/
+ protected function revert_counter_increment(): void
+ {
+ if ($this->content_set
+ && $this->get_node()->nodeName !== "body"
+ && ($decrement = $this->get_style()->counter_increment) !== "none"
+ ) {
+ $this->decrement_counters($decrement);
+ }
+ }
+
+ // Getters -----------
+
function get_id()
{
return $this->_frame->get_id();
@@ -206,45 +296,29 @@ abstract class AbstractFrameDecorator extends Frame
return $this->_frame;
}
- /**
- * @return DOMElement|DOMText
- */
function get_node()
{
return $this->_frame->get_node();
}
- /**
- * @return Style
- */
function get_style()
{
return $this->_frame->get_style();
}
/**
- * @return Style
+ * @deprecated
*/
function get_original_style()
{
- return $this->_frame->get_original_style();
+ return $this->_frame->get_style();
}
- /**
- * @param integer $i
- *
- * @return array|float
- */
function get_containing_block($i = null)
{
return $this->_frame->get_containing_block($i);
}
- /**
- * @param integer $i
- *
- * @return array|float
- */
function get_position($i = null)
{
return $this->_frame->get_position($i);
@@ -258,110 +332,66 @@ abstract class AbstractFrameDecorator extends Frame
return $this->_dompdf;
}
- /**
- * @return float
- */
- function get_margin_height()
- {
- return $this->_frame->get_margin_height();
- }
-
- /**
- * @return float
- */
- function get_margin_width()
+ public function get_margin_width(): float
{
return $this->_frame->get_margin_width();
}
- /**
- * @return array
- */
- function get_content_box()
+ public function get_margin_height(): float
+ {
+ return $this->_frame->get_margin_height();
+ }
+
+ public function get_content_box(): array
{
return $this->_frame->get_content_box();
}
- /**
- * @return array
- */
- function get_padding_box()
+ public function get_padding_box(): array
{
return $this->_frame->get_padding_box();
}
- /**
- * @return array
- */
- function get_border_box()
+ public function get_border_box(): array
{
return $this->_frame->get_border_box();
}
- /**
- * @param integer $id
- */
function set_id($id)
{
$this->_frame->set_id($id);
}
- /**
- * @param Style $style
- */
- function set_style(Style $style)
+ public function set_style(Style $style): void
{
$this->_frame->set_style($style);
}
- /**
- * @param float $x
- * @param float $y
- * @param float $w
- * @param float $h
- */
function set_containing_block($x = null, $y = null, $w = null, $h = null)
{
$this->_frame->set_containing_block($x, $y, $w, $h);
}
- /**
- * @param float $x
- * @param float $y
- */
function set_position($x = null, $y = null)
{
$this->_frame->set_position($x, $y);
}
- /**
- * @return bool
- */
function is_auto_height()
{
return $this->_frame->is_auto_height();
}
- /**
- * @return bool
- */
function is_auto_width()
{
return $this->_frame->is_auto_width();
}
- /**
- * @return string
- */
function __toString()
{
return $this->_frame->__toString();
}
- /**
- * @param Frame $child
- * @param bool $update_node
- */
function prepend_child(Frame $child, $update_node = true)
{
while ($child instanceof AbstractFrameDecorator) {
@@ -371,10 +401,6 @@ abstract class AbstractFrameDecorator extends Frame
$this->_frame->prepend_child($child, $update_node);
}
- /**
- * @param Frame $child
- * @param bool $update_node
- */
function append_child(Frame $child, $update_node = true)
{
while ($child instanceof AbstractFrameDecorator) {
@@ -384,11 +410,6 @@ abstract class AbstractFrameDecorator extends Frame
$this->_frame->append_child($child, $update_node);
}
- /**
- * @param Frame $new_child
- * @param Frame $ref
- * @param bool $update_node
- */
function insert_child_before(Frame $new_child, Frame $ref, $update_node = true)
{
while ($new_child instanceof AbstractFrameDecorator) {
@@ -402,11 +423,6 @@ abstract class AbstractFrameDecorator extends Frame
$this->_frame->insert_child_before($new_child, $ref, $update_node);
}
- /**
- * @param Frame $new_child
- * @param Frame $ref
- * @param bool $update_node
- */
function insert_child_after(Frame $new_child, Frame $ref, $update_node = true)
{
$insert_frame = $new_child;
@@ -422,12 +438,6 @@ abstract class AbstractFrameDecorator extends Frame
$this->_frame->insert_child_after($insert_frame, $reference_frame, $update_node);
}
- /**
- * @param Frame $child
- * @param bool $update_node
- *
- * @return Frame
- */
function remove_child(Frame $child, $update_node = true)
{
while ($child instanceof AbstractFrameDecorator) {
@@ -543,11 +553,19 @@ abstract class AbstractFrameDecorator extends Frame
}
/**
- * @return FrameTreeList
+ * @return FrameListIterator
*/
- function get_subtree()
+ public function get_children(): FrameListIterator
{
- return new FrameTreeList($this);
+ return new FrameListIterator($this);
+ }
+
+ /**
+ * @return FrameTreeIterator
+ */
+ function get_subtree(): FrameTreeIterator
+ {
+ return new FrameTreeIterator($this);
}
function set_positioner(AbstractPositioner $posn)
@@ -567,7 +585,15 @@ abstract class AbstractFrameDecorator extends Frame
}
/**
- * @return \Dompdf\FrameReflower\AbstractFrameReflower
+ * @return AbstractPositioner
+ */
+ function get_positioner()
+ {
+ return $this->_positioner;
+ }
+
+ /**
+ * @return AbstractFrameReflower
*/
function get_reflower()
{
@@ -600,6 +626,10 @@ abstract class AbstractFrameDecorator extends Frame
function find_block_parent()
{
// Find our nearest block level parent
+ if (isset($this->_block_parent)) {
+ return $this->_block_parent;
+ }
+
$p = $this->get_parent();
while ($p) {
@@ -616,12 +646,16 @@ abstract class AbstractFrameDecorator extends Frame
/**
* @return AbstractFrameDecorator
*/
- function find_positionned_parent()
+ function find_positioned_parent()
{
- // Find our nearest relative positionned parent
+ // Find our nearest relative positioned parent
+ if (isset($this->_positioned_parent)) {
+ return $this->_positioned_parent;
+ }
+
$p = $this->get_parent();
while ($p) {
- if ($p->is_positionned()) {
+ if ($p->is_positioned()) {
break;
}
@@ -629,55 +663,28 @@ abstract class AbstractFrameDecorator extends Frame
}
if (!$p) {
- $p = $this->_root->get_first_child(); //
+ $p = $this->_root;
}
- return $this->_positionned_parent = $p;
+ return $this->_positioned_parent = $p;
}
/**
- * split this frame at $child.
+ * Split this frame at $child.
* The current frame is cloned and $child and all children following
* $child are added to the clone. The clone is then passed to the
* current frame's parent->split() method.
*
- * @param Frame $child
- * @param boolean $force_pagebreak
+ * @param Frame|null $child
+ * @param bool $page_break
+ * @param bool $forced Whether the page break is forced.
*
* @throws Exception
- * @return void
*/
- function split(Frame $child = null, $force_pagebreak = false)
+ public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
{
- // decrement any counters that were incremented on the current node, unless that node is the body
- $style = $this->_frame->get_style();
- if (
- $this->_frame->get_node()->nodeName !== "body" &&
- $style->counter_increment &&
- ($decrement = $style->counter_increment) !== "none"
- ) {
- $this->decrement_counters($decrement);
- }
-
if (is_null($child)) {
- // check for counter increment on :before content (always a child of the selected element @link AbstractFrameReflower::_set_content)
- // this can push the current node to the next page before counter rules have bubbled up (but only if
- // it's been rendered, thus the position check)
- if (!$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id")) {
- foreach ($this->_frame->get_children() as $child) {
- if (
- $this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() &&
- $child->get_position('x') !== null
- ) {
- $style = $child->get_style();
- if ($style->counter_increment && ($decrement = $style->counter_increment) !== "none") {
- $this->decrement_counters($decrement);
- }
- }
- }
- }
- $this->get_parent()->split($this, $force_pagebreak);
-
+ $this->get_parent()->split($this, $page_break, $forced);
return;
}
@@ -685,104 +692,118 @@ abstract class AbstractFrameDecorator extends Frame
throw new Exception("Unable to split: frame is not a child of this one.");
}
- $node = $this->_frame->get_node();
+ $this->revert_counter_increment();
- if ($node instanceof DOMElement && $node->hasAttribute("id")) {
- $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
- $node->removeAttribute("id");
+ $node = $this->_frame->get_node();
+ $split = $this->copy($node->cloneNode());
+
+ $style = $this->_frame->get_style();
+ $split_style = $split->get_style();
+
+ // Truncate the box decoration at the split, except for the body
+ if ($node->nodeName !== "body") {
+ // Clear bottom decoration of original frame
+ $style->margin_bottom = 0.0;
+ $style->padding_bottom = 0.0;
+ $style->border_bottom_width = 0.0;
+ $style->border_bottom_left_radius = 0.0;
+ $style->border_bottom_right_radius = 0.0;
+
+ // Clear top decoration of split frame
+ $split_style->margin_top = 0.0;
+ $split_style->padding_top = 0.0;
+ $split_style->border_top_width = 0.0;
+ $split_style->border_top_left_radius = 0.0;
+ $split_style->border_top_right_radius = 0.0;
+ $split_style->page_break_before = "auto";
}
- $split = $this->copy($node->cloneNode());
- $split->reset();
- $split->get_original_style()->text_indent = 0;
- $split->_splitted = true;
+ $split_style->text_indent = 0.0;
+ $split_style->counter_reset = "none";
+
+ $this->is_split = true;
+ $split->is_split_off = true;
$split->_already_pushed = true;
- // The body's properties must be kept
- if ($node->nodeName !== "body") {
- // Style reset on the first and second parts
- $style = $this->_frame->get_style();
- $style->margin_bottom = 0;
- $style->padding_bottom = 0;
- $style->border_bottom = 0;
-
- // second
- $orig_style = $split->get_original_style();
- $orig_style->text_indent = 0;
- $orig_style->margin_top = 0;
- $orig_style->padding_top = 0;
- $orig_style->border_top = 0;
- $orig_style->page_break_before = "auto";
- }
-
- // recalculate the float offsets after paging
$this->get_parent()->insert_child_after($split, $this);
+
if ($this instanceof Block) {
- foreach ($this->get_line_boxes() as $index => $line_box) {
+ // Remove the frames that will be moved to the new split node from
+ // the line boxes
+ $this->remove_frames_from_line($child);
+
+ // recalculate the float offsets after paging
+ foreach ($this->get_line_boxes() as $line_box) {
$line_box->get_float_offsets();
}
}
- // Add $frame and all following siblings to the new split node
+ if (!$forced) {
+ // Reset top margin in case of an unforced page break
+ // https://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
+ $child->get_style()->margin_top = 0.0;
+ }
+
+ // Add $child and all following siblings to the new split node
$iter = $child;
while ($iter) {
$frame = $iter;
$iter = $iter->get_next_sibling();
$frame->reset();
- $frame->_parent = $split;
$split->append_child($frame);
-
- // recalculate the float offsets
- if ($frame instanceof Block) {
- foreach ($frame->get_line_boxes() as $index => $line_box) {
- $line_box->get_float_offsets();
- }
- }
}
- $this->get_parent()->split($split, $force_pagebreak);
+ $this->get_parent()->split($split, $page_break, $forced);
- // If this node resets a counter save the current value to use when rendering on the next page
- if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") {
- $vars = preg_split('/\s+/', trim($reset), 2);
- $split->_counters['__' . $vars[0]] = $this->lookup_counter_frame($vars[0])->_counters[$vars[0]];
+ // Preserve the current counter values. This must be done after the
+ // parent split, as counters get reset on frame reset
+ $split->_counters = $this->_counters;
+ }
+
+ /**
+ * @param array $counters
+ */
+ public function reset_counters(array $counters): void
+ {
+ foreach ($counters as $id => $value) {
+ $this->reset_counter($id, $value);
}
}
/**
* @param string $id
- * @param int $value
+ * @param int $value
*/
- function reset_counter($id = self::DEFAULT_COUNTER, $value = 0)
+ public function reset_counter(string $id = self::DEFAULT_COUNTER, int $value = 0): void
{
- $this->get_parent()->_counters[$id] = intval($value);
+ $this->get_parent()->_counters[$id] = $value;
}
/**
- * @param $counters
+ * @param array $counters
*/
- function decrement_counters($counters)
+ public function decrement_counters(array $counters): void
{
foreach ($counters as $id => $increment) {
- $this->increment_counter($id, intval($increment) * -1);
+ $this->increment_counter($id, $increment * -1);
}
}
/**
- * @param $counters
+ * @param array $counters
*/
- function increment_counters($counters)
+ public function increment_counters(array $counters): void
{
foreach ($counters as $id => $increment) {
- $this->increment_counter($id, intval($increment));
+ $this->increment_counter($id, $increment);
}
}
/**
* @param string $id
- * @param int $increment
+ * @param int $increment
*/
- function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1)
+ public function increment_counter(string $id = self::DEFAULT_COUNTER, int $increment = 1): void
{
$counter_frame = $this->lookup_counter_frame($id);
@@ -826,7 +847,7 @@ abstract class AbstractFrameDecorator extends Frame
*
* TODO: What version is the best : this one or the one in ListBullet ?
*/
- function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal")
+ function counter_value(string $id = self::DEFAULT_COUNTER, string $type = "decimal")
{
$type = mb_strtolower($type);
@@ -852,11 +873,11 @@ abstract class AbstractFrameDecorator extends Frame
case "lower-latin":
case "lower-alpha":
- return chr(($value % 26) + ord('a') - 1);
+ return chr((($value - 1) % 26) + ord('a'));
case "upper-latin":
case "upper-alpha":
- return chr(($value % 26) + ord('A') - 1);
+ return chr((($value - 1) % 26) + ord('A'));
case "lower-greek":
return Helpers::unichr($value + 944);
@@ -866,20 +887,17 @@ abstract class AbstractFrameDecorator extends Frame
}
}
- /**
- *
- */
final function position()
{
$this->_positioner->position($this);
}
/**
- * @param $offset_x
- * @param $offset_y
- * @param bool $ignore_self
+ * @param float $offset_x
+ * @param float $offset_y
+ * @param bool $ignore_self
*/
- final function move($offset_x, $offset_y, $ignore_self = false)
+ final function move(float $offset_x, float $offset_y, bool $ignore_self = false): void
{
$this->_positioner->move($this, $offset_x, $offset_y, $ignore_self);
}
@@ -898,18 +916,8 @@ abstract class AbstractFrameDecorator extends Frame
/**
* @return array
*/
- final function get_min_max_width()
+ final public function get_min_max_width(): array
{
return $this->_reflower->get_min_max_width();
}
-
- /**
- * Determine current frame width based on contents
- *
- * @return float
- */
- final function calculate_auto_width()
- {
- return $this->_reflower->calculate_auto_width();
- }
}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Block.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Block.php
new file mode 100644
index 000000000..1fcf134d8
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Block.php
@@ -0,0 +1,256 @@
+_line_boxes = [new LineBox($this)];
+ $this->_cl = 0;
+ $this->dangling_markers = [];
+ }
+
+ function reset()
+ {
+ parent::reset();
+
+ $this->_line_boxes = [new LineBox($this)];
+ $this->_cl = 0;
+ $this->dangling_markers = [];
+ }
+
+ /**
+ * @return LineBox
+ */
+ function get_current_line_box()
+ {
+ return $this->_line_boxes[$this->_cl];
+ }
+
+ /**
+ * @return int
+ */
+ function get_current_line_number()
+ {
+ return $this->_cl;
+ }
+
+ /**
+ * @return LineBox[]
+ */
+ function get_line_boxes()
+ {
+ return $this->_line_boxes;
+ }
+
+ /**
+ * @param int $line_number
+ * @return int
+ */
+ function set_current_line_number($line_number)
+ {
+ $line_boxes_count = count($this->_line_boxes);
+ $cl = max(min($line_number, $line_boxes_count), 0);
+ return ($this->_cl = $cl);
+ }
+
+ /**
+ * @param int $i
+ */
+ function clear_line($i)
+ {
+ if (isset($this->_line_boxes[$i])) {
+ unset($this->_line_boxes[$i]);
+ }
+ }
+
+ /**
+ * @param Frame $frame
+ * @return LineBox|null
+ */
+ public function add_frame_to_line(Frame $frame): ?LineBox
+ {
+ $current_line = $this->_line_boxes[$this->_cl];
+ $frame->set_containing_line($current_line);
+
+ // Inline frames are currently treated as wrappers, and are not actually
+ // added to the line
+ if ($frame instanceof Inline) {
+ return null;
+ }
+
+ $current_line->add_frame($frame);
+
+ $this->increase_line_width($frame->get_margin_width());
+ $this->maximize_line_height($frame->get_margin_height(), $frame);
+
+ // Add any dangling list markers to the first line box if it is inline
+ if ($this->_cl === 0 && $current_line->inline
+ && $this->dangling_markers !== []
+ ) {
+ foreach ($this->dangling_markers as $marker) {
+ $current_line->add_list_marker($marker);
+ $this->maximize_line_height($marker->get_margin_height(), $marker);
+ }
+
+ $this->dangling_markers = [];
+ }
+
+ return $current_line;
+ }
+
+ /**
+ * Remove the given frame and all following frames and lines from the block.
+ *
+ * @param Frame $frame
+ */
+ public function remove_frames_from_line(Frame $frame): void
+ {
+ // Inline frames are not added to line boxes themselves, only their
+ // text frame children
+ $actualFrame = $frame;
+ while ($actualFrame !== null && $actualFrame instanceof Inline) {
+ $actualFrame = $actualFrame->get_first_child();
+ }
+
+ if ($actualFrame === null) {
+ return;
+ }
+
+ // Search backwards through the lines for $frame
+ $frame = $actualFrame;
+ $i = $this->_cl;
+ $j = null;
+
+ while ($i > 0) {
+ $line = $this->_line_boxes[$i];
+ foreach ($line->get_frames() as $index => $f) {
+ if ($frame === $f) {
+ $j = $index;
+ break 2;
+ }
+ }
+ $i--;
+ }
+
+ if ($j === null) {
+ return;
+ }
+
+ // Remove all lines that follow
+ for ($k = $this->_cl; $k > $i; $k--) {
+ unset($this->_line_boxes[$k]);
+ }
+
+ // Remove the line, if it is empty
+ if ($j > 0) {
+ $line->remove_frames($j);
+ } else {
+ unset($this->_line_boxes[$i]);
+ }
+
+ // Reset array indices
+ $this->_line_boxes = array_values($this->_line_boxes);
+ $this->_cl = count($this->_line_boxes) - 1;
+ }
+
+ /**
+ * @param float $w
+ */
+ public function increase_line_width(float $w): void
+ {
+ $this->_line_boxes[$this->_cl]->w += $w;
+ }
+
+ /**
+ * @param float $val
+ * @param Frame $frame
+ */
+ public function maximize_line_height(float $val, Frame $frame): void
+ {
+ if ($val > $this->_line_boxes[$this->_cl]->h) {
+ $this->_line_boxes[$this->_cl]->tallest_frame = $frame;
+ $this->_line_boxes[$this->_cl]->h = $val;
+ }
+ }
+
+ /**
+ * @param bool $br
+ */
+ public function add_line(bool $br = false): void
+ {
+ $line = $this->_line_boxes[$this->_cl];
+
+ $line->br = $br;
+ $y = $line->y + $line->h;
+
+ $new_line = new LineBox($this, $y);
+
+ $this->_line_boxes[++$this->_cl] = $new_line;
+ }
+
+ /**
+ * @param ListBullet $marker
+ */
+ public function add_dangling_marker(ListBullet $marker): void
+ {
+ $this->dangling_markers[] = $marker;
+ }
+
+ /**
+ * Inherit any dangling markers from the parent block.
+ *
+ * @param Block $block
+ */
+ public function inherit_dangling_markers(self $block): void
+ {
+ if ($block->dangling_markers !== []) {
+ $this->dangling_markers = $block->dangling_markers;
+ $block->dangling_markers = [];
+ }
+ }
+}
diff --git a/library/vendor/dompdf/src/FrameDecorator/Image.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Image.php
similarity index 62%
rename from library/vendor/dompdf/src/FrameDecorator/Image.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Image.php
index 0dc62e966..92ac491a4 100644
--- a/library/vendor/dompdf/src/FrameDecorator/Image.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Image.php
@@ -1,15 +1,14 @@
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameDecorator;
use Dompdf\Dompdf;
use Dompdf\Frame;
+use Dompdf\Helpers;
use Dompdf\Image\Cache;
/**
@@ -56,18 +55,48 @@ class Image extends AbstractFrameDecorator
$dompdf->getProtocol(),
$dompdf->getBaseHost(),
$dompdf->getBasePath(),
- $dompdf
+ $dompdf->getOptions()
);
if (Cache::is_broken($this->_image_url) &&
$alt = $frame->get_node()->getAttribute("alt")
) {
+ $fontMetrics = $dompdf->getFontMetrics();
$style = $frame->get_style();
- $style->width = (4 / 3) * $dompdf->getFontMetrics()->getTextWidth($alt, $style->font_family, $style->font_size, $style->word_spacing);
- $style->height = $dompdf->getFontMetrics()->getFontHeight($style->font_family, $style->font_size);
+ $font = $style->font_family;
+ $size = $style->font_size;
+ $word_spacing = $style->word_spacing;
+ $letter_spacing = $style->letter_spacing;
+
+ $style->width = (4 / 3) * $fontMetrics->getTextWidth($alt, $font, $size, $word_spacing, $letter_spacing);
+ $style->height = $fontMetrics->getFontHeight($font, $size);
}
}
+ /**
+ * Get the intrinsic pixel dimensions of the image.
+ *
+ * @return array Width and height as `float|int`.
+ */
+ public function get_intrinsic_dimensions(): array
+ {
+ [$width, $height] = Helpers::dompdf_getimagesize($this->_image_url, $this->_dompdf->getHttpContext());
+
+ return [$width, $height];
+ }
+
+ /**
+ * Resample the given pixel length according to dpi.
+ *
+ * @param float|int $length
+ * @return float
+ */
+ public function resample($length): float
+ {
+ $dpi = $this->_dompdf->getOptions()->getDpi();
+ return ($length * 72) / $dpi;
+ }
+
/**
* Return the image's url
*
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Inline.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Inline.php
new file mode 100644
index 000000000..668d795e0
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Inline.php
@@ -0,0 +1,121 @@
+get_style();
+ $font = $style->font_family;
+ $size = $style->font_size;
+ $fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size);
+
+ return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight;
+ }
+
+ public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
+ {
+ if (is_null($child)) {
+ $this->get_parent()->split($this, $page_break, $forced);
+ return;
+ }
+
+ if ($child->get_parent() !== $this) {
+ throw new Exception("Unable to split: frame is not a child of this one.");
+ }
+
+ $this->revert_counter_increment();
+ $node = $this->_frame->get_node();
+ $split = $this->copy($node->cloneNode());
+
+ $style = $this->_frame->get_style();
+ $split_style = $split->get_style();
+
+ // Unset the current node's right style properties
+ $style->margin_right = 0.0;
+ $style->padding_right = 0.0;
+ $style->border_right_width = 0.0;
+ $style->border_top_right_radius = 0.0;
+ $style->border_bottom_right_radius = 0.0;
+
+ // Unset the split node's left style properties since we don't want them
+ // to propagate
+ $split_style->margin_left = 0.0;
+ $split_style->padding_left = 0.0;
+ $split_style->border_left_width = 0.0;
+ $split_style->border_top_left_radius = 0.0;
+ $split_style->border_bottom_left_radius = 0.0;
+
+ // If this is a generated node don't propagate the content style
+ if ($split->get_node()->nodeName == "dompdf_generated") {
+ $split_style->content = "normal";
+ }
+
+ //On continuation of inline element on next line,
+ //don't repeat non-horizontally repeatable background images
+ //See e.g. in testcase image_variants, long descriptions
+ if (($url = $style->background_image) && $url !== "none"
+ && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-x"
+ ) {
+ $split_style->background_image = "none";
+ }
+
+ $this->get_parent()->insert_child_after($split, $this);
+
+ // Add $child and all following siblings to the new split node
+ $iter = $child;
+ while ($iter) {
+ $frame = $iter;
+ $iter = $iter->get_next_sibling();
+ $frame->reset();
+ $split->append_child($frame);
+ }
+
+ $parent = $this->get_parent();
+
+ if ($page_break) {
+ $parent->split($split, $page_break, $forced);
+ } elseif ($parent instanceof Inline) {
+ $parent->split($split);
+ }
+ }
+
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php
new file mode 100644
index 000000000..703f46767
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php
@@ -0,0 +1,117 @@
+_frame->get_style();
+
+ if ($style->list_style_type === "none") {
+ return 0.0;
+ }
+
+ return $style->font_size * self::BULLET_SIZE;
+ }
+
+ /**
+ * Get the height of the bullet symbol.
+ *
+ * @return float
+ */
+ public function get_height(): float
+ {
+ $style = $this->_frame->get_style();
+
+ if ($style->list_style_type === "none") {
+ return 0.0;
+ }
+
+ return $style->font_size * self::BULLET_SIZE;
+ }
+
+ /**
+ * Get the width of the bullet, including indentation.
+ */
+ public function get_margin_width(): float
+ {
+ $style = $this->get_style();
+
+ if ($style->list_style_type === "none") {
+ return 0.0;
+ }
+
+ return $style->font_size * (self::BULLET_SIZE + self::MARKER_INDENT);
+ }
+
+ /**
+ * Get the line height for the bullet.
+ *
+ * This increases the height of the corresponding line box when necessary.
+ */
+ public function get_margin_height(): float
+ {
+ $style = $this->get_style();
+
+ if ($style->list_style_type === "none") {
+ return 0.0;
+ }
+
+ // TODO: This is a copy of `FrameDecorator\Text::get_margin_height()`
+ // Would be nice to properly refactor that at some point
+ $font = $style->font_family;
+ $size = $style->font_size;
+ $fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size);
+
+ return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBulletImage.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBulletImage.php
new file mode 100644
index 000000000..d921929c2
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBulletImage.php
@@ -0,0 +1,112 @@
+get_style();
+ $url = $style->list_style_image;
+ $frame->get_node()->setAttribute("src", $url);
+ $this->_img = new Image($frame, $dompdf);
+ parent::__construct($this->_img, $dompdf);
+
+ $url = $this->_img->get_image_url();
+
+ if (Cache::is_broken($url)) {
+ $this->_width = parent::get_width();
+ $this->_height = parent::get_height();
+ } else {
+ // Resample the bullet image to be consistent with 'auto' sized images
+ [$width, $height] = $this->_img->get_intrinsic_dimensions();
+ $this->_width = $this->_img->resample($width);
+ $this->_height = $this->_img->resample($height);
+ }
+ }
+
+ public function get_width(): float
+ {
+ return $this->_width;
+ }
+
+ public function get_height(): float
+ {
+ return $this->_height;
+ }
+
+ public function get_margin_width(): float
+ {
+ $style = $this->get_style();
+ return $this->_width + $style->font_size * self::MARKER_INDENT;
+ }
+
+ public function get_margin_height(): float
+ {
+ $fontMetrics = $this->_dompdf->getFontMetrics();
+ $style = $this->get_style();
+ $font = $style->font_family;
+ $size = $style->font_size;
+ $fontHeight = $fontMetrics->getFontHeight($font, $size);
+ $baseline = $fontMetrics->getFontBaseline($font, $size);
+
+ // This is the same factor as used in
+ // `FrameDecorator\Text::get_margin_height()`
+ $f = $style->line_height / ($size > 0 ? $size : 1);
+
+ // FIXME: Tries to approximate replacing the space above the font
+ // baseline with the image
+ return $f * ($fontHeight - $baseline) + $this->_height;
+ }
+
+ /**
+ * Return image url
+ *
+ * @return string
+ */
+ function get_image_url()
+ {
+ return $this->_img->get_image_url();
+ }
+}
diff --git a/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/NullFrameDecorator.php
similarity index 87%
rename from library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/NullFrameDecorator.php
index e3457cfe7..f08381675 100644
--- a/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/NullFrameDecorator.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameDecorator;
diff --git a/library/vendor/dompdf/src/FrameDecorator/Page.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Page.php
similarity index 62%
rename from library/vendor/dompdf/src/FrameDecorator/Page.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Page.php
index 39921aba8..25ef2408f 100644
--- a/library/vendor/dompdf/src/FrameDecorator/Page.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Page.php
@@ -1,13 +1,11 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameDecorator;
-use Dompdf\Css\Style;
use Dompdf\Dompdf;
use Dompdf\Helpers;
use Dompdf\Frame;
@@ -16,18 +14,18 @@ use Dompdf\Renderer;
/**
* Decorates frames for page layout
*
- * @access private
* @package dompdf
*/
class Page extends AbstractFrameDecorator
{
-
/**
- * y value of bottom page margin
+ * The y value of the bottom edge of the page area.
+ *
+ * https://www.w3.org/TR/CSS21/page.html#page-margins
*
* @var float
*/
- protected $_bottom_page_margin;
+ protected $bottom_page_edge;
/**
* Flag indicating page is full.
@@ -55,7 +53,7 @@ class Page extends AbstractFrameDecorator
*
* @var array
*/
- protected $_floating_frames = array();
+ protected $_floating_frames = [];
//........................................................................
@@ -70,7 +68,7 @@ class Page extends AbstractFrameDecorator
parent::__construct($frame, $dompdf);
$this->_page_full = false;
$this->_in_table = 0;
- $this->_bottom_page_margin = null;
+ $this->bottom_page_edge = null;
}
/**
@@ -94,20 +92,16 @@ class Page extends AbstractFrameDecorator
}
/**
- * Set the frame's containing block. Overridden to set $this->_bottom_page_margin.
- *
- * @param float $x
- * @param float $y
- * @param float $w
- * @param float $h
+ * Calculate the bottom edge of the page area after margins have been
+ * applied for the current page.
*/
- function set_containing_block($x = null, $y = null, $w = null, $h = null)
+ public function calculate_bottom_page_edge(): void
{
- parent::set_containing_block($x, $y, $w, $h);
- //$w = $this->get_containing_block("w");
- if (isset($h)) {
- $this->_bottom_page_margin = $h;
- } // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w);
+ [, , , $cbh] = $this->get_containing_block();
+ $style = $this->get_style();
+ $margin_bottom = (float) $style->length_in_pt($style->margin_bottom, $cbh);
+
+ $this->bottom_page_edge = $cbh - $margin_bottom;
}
/**
@@ -125,7 +119,7 @@ class Page extends AbstractFrameDecorator
*/
function next_page()
{
- $this->_floating_frames = array();
+ $this->_floating_frames = [];
$this->_renderer->new_page();
$this->_page_full = false;
}
@@ -158,64 +152,70 @@ class Page extends AbstractFrameDecorator
/**
* Check if a forced page break is required before $frame. This uses the
- * frame's page_break_before property as well as the preceeding frame's
+ * frame's page_break_before property as well as the preceding frame's
* page_break_after property.
*
* @link http://www.w3.org/TR/CSS21/page.html#forced
*
- * @param Frame $frame the frame to check
+ * @param AbstractFrameDecorator $frame the frame to check
*
- * @return bool true if a page break occured
+ * @return bool true if a page break occurred
*/
function check_forced_page_break(Frame $frame)
{
- // Skip check if page is already split
- if ($this->_page_full) {
- return null;
- }
-
- $block_types = array("block", "list-item", "table", "inline");
- $page_breaks = array("always", "left", "right");
-
- $style = $frame->get_style();
-
- if (!in_array($style->display, $block_types)) {
+ // Skip check if page is already split and for the body
+ if ($this->_page_full || $frame->get_node()->nodeName === "body") {
return false;
}
- // Find the previous block-level sibling
- $prev = $frame->get_prev_sibling();
+ $page_breaks = ["always", "left", "right"];
+ $style = $frame->get_style();
- while ($prev && !in_array($prev->get_style()->display, $block_types)) {
+ if (($frame->is_block_level() || $style->display === "table-row")
+ && in_array($style->page_break_before, $page_breaks, true)
+ ) {
+ // Prevent cascading splits
+ $frame->split(null, true, true);
+ $style->page_break_before = "auto";
+ $this->_page_full = true;
+ $frame->_already_pushed = true;
+
+ return true;
+ }
+
+ // Find the preceding block-level sibling (or table row). Inline
+ // elements are treated as if wrapped in an anonymous block container
+ // here. See https://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level
+ $prev = $frame->get_prev_sibling();
+ while ($prev && (($prev->is_text_node() && $prev->get_node()->nodeValue === "")
+ || $prev->get_node()->nodeName === "bullet")
+ ) {
$prev = $prev->get_prev_sibling();
}
- if (in_array($style->page_break_before, $page_breaks)) {
- // Prevent cascading splits
- $frame->split(null, true);
- // We have to grab the style again here because split() resets
- // $frame->style to the frame's original style.
- $frame->get_style()->page_break_before = "auto";
- $this->_page_full = true;
- $frame->_already_pushed = true;
+ if ($prev && ($prev->is_block_level() || $prev->get_style()->display === "table-row")) {
+ if (in_array($prev->get_style()->page_break_after, $page_breaks, true)) {
+ // Prevent cascading splits
+ $frame->split(null, true, true);
+ $prev->get_style()->page_break_after = "auto";
+ $this->_page_full = true;
+ $frame->_already_pushed = true;
- return true;
- }
+ return true;
+ }
- if ($prev && in_array($prev->get_style()->page_break_after, $page_breaks)) {
- // Prevent cascading splits
- $frame->split(null, true);
- $prev->get_style()->page_break_after = "auto";
- $this->_page_full = true;
- $frame->_already_pushed = true;
-
- return true;
- }
-
- if ($prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body") {
$prev_last_child = $prev->get_last_child();
- if (in_array($prev_last_child->get_style()->page_break_after, $page_breaks)) {
- $frame->split(null, true);
+ while ($prev_last_child && (($prev_last_child->is_text_node() && $prev_last_child->get_node()->nodeValue === "")
+ || $prev_last_child->get_node()->nodeName === "bullet")
+ ) {
+ $prev_last_child = $prev_last_child->get_prev_sibling();
+ }
+
+ if ($prev_last_child
+ && $prev_last_child->is_block_level()
+ && in_array($prev_last_child->get_style()->page_break_after, $page_breaks, true)
+ ) {
+ $frame->split(null, true, true);
$prev_last_child->get_style()->page_break_after = "auto";
$this->_page_full = true;
$frame->_already_pushed = true;
@@ -227,16 +227,45 @@ class Page extends AbstractFrameDecorator
return false;
}
+ /**
+ * Check for a gap between the top content edge of a frame and its child
+ * content.
+ *
+ * Additionally, the top margin, border, and padding of the frame must fit
+ * on the current page.
+ *
+ * @param float $childPos The top margin or line-box edge of the child content.
+ * @param Frame $frame The parent frame to check.
+ * @return bool
+ */
+ protected function hasGap(float $childPos, Frame $frame): bool
+ {
+ $style = $frame->get_style();
+ $cbw = $frame->get_containing_block("w");
+ $contentEdge = $frame->get_position("y") + (float) $style->length_in_pt([
+ $style->margin_top,
+ $style->border_top_width,
+ $style->padding_top
+ ], $cbw);
+
+ return Helpers::lengthGreater($childPos, $contentEdge)
+ && Helpers::lengthLessOrEqual($contentEdge, $this->bottom_page_edge);
+ }
+
/**
* Determine if a page break is allowed before $frame
* http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
*
* In the normal flow, page breaks can occur at the following places:
*
- * 1. In the vertical margin between block boxes. When a page
- * break occurs here, the used values of the relevant
- * 'margin-top' and 'margin-bottom' properties are set to '0'.
- * 2. Between line boxes inside a block box.
+ * 1. In the vertical margin between block boxes. When an
+ * unforced page break occurs here, the used values of the
+ * relevant 'margin-top' and 'margin-bottom' properties are set
+ * to '0'. When a forced page break occurs here, the used value
+ * of the relevant 'margin-bottom' property is set to '0'; the
+ * relevant 'margin-top' used value may either be set to '0' or
+ * retained.
+ * 2. Between line boxes inside a block container box.
* 3. Between the content edge of a block container box and the
* outer edges of its child content (margin edges of block-level
* children or line box edges for inline-level children) if there
@@ -245,49 +274,45 @@ class Page extends AbstractFrameDecorator
* These breaks are subject to the following rules:
*
* * Rule A: Breaking at (1) is allowed only if the
- * 'page-break-after' and 'page-break-before' properties of
- * all the elements generating boxes that meet at this margin
- * allow it, which is when at least one of them has the value
- * 'always', 'left', or 'right', or when all of them are
- * 'auto'.
+ * 'page-break-after' and 'page-break-before' properties of all
+ * the elements generating boxes that meet at this margin allow
+ * it, which is when at least one of them has the value
+ * 'always', 'left', or 'right', or when all of them are 'auto'.
*
- * * Rule B: However, if all of them are 'auto' and the
- * nearest common ancestor of all the elements has a
- * 'page-break-inside' value of 'avoid', then breaking here is
- * not allowed.
+ * * Rule B: However, if all of them are 'auto' and a common
+ * ancestor of all the elements has a 'page-break-inside' value
+ * of 'avoid', then breaking here is not allowed.
*
- * * Rule C: Breaking at (2) is allowed only if the number of
- * line boxes between the break and the start of the enclosing
- * block box is the value of 'orphans' or more, and the number
- * of line boxes between the break and the end of the box is
- * the value of 'widows' or more.
+ * * Rule C: Breaking at (2) is allowed only if the number of line
+ * boxes between the break and the start of the enclosing block
+ * box is the value of 'orphans' or more, and the number of line
+ * boxes between the break and the end of the box is the value
+ * of 'widows' or more.
*
- * * Rule D: In addition, breaking at (2) is allowed only if
- * the 'page-break-inside' property is 'auto'.
+ * * Rule D: In addition, breaking at (2) or (3) is allowed only
+ * if the 'page-break-inside' property of the element and all
+ * its ancestors is 'auto'.
*
- * If the above doesn't provide enough break points to keep
- * content from overflowing the page boxes, then rules B and D are
+ * If the above does not provide enough break points to keep content
+ * from overflowing the page boxes, then rules A, B and D are
* dropped in order to find additional breakpoints.
*
- * If that still does not lead to sufficient break points, rules A
- * and C are dropped as well, to find still more break points.
+ * If that still does not lead to sufficient break points, rule C is
+ * dropped as well, to find still more break points.
*
- * We will also allow breaks between table rows. However, when
- * splitting a table, the table headers should carry over to the
- * next page (but they don't yet).
+ * We also allow breaks between table rows.
*
- * @param Frame $frame the frame to check
+ * @param AbstractFrameDecorator $frame the frame to check
*
* @return bool true if a break is allowed, false otherwise
*/
protected function _page_break_allowed(Frame $frame)
{
- $block_types = array("block", "list-item", "table", "-dompdf-image");
Helpers::dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName . ")");
$display = $frame->get_style()->display;
// Block Frames (1):
- if (in_array($display, $block_types)) {
+ if ($frame->is_block_level() || $display === "-dompdf-image") {
// Avoid breaks within table-cells
if ($this->_in_table > ($display === "table" ? 1 : 0)) {
@@ -296,34 +321,42 @@ class Page extends AbstractFrameDecorator
return false;
}
- // Rules A & B
-
+ // Rule A
if ($frame->get_style()->page_break_before === "avoid") {
Helpers::dompdf_debug("page-break", "before: avoid");
return false;
}
- // Find the preceeding block-level sibling
+ // Find the preceding block-level sibling. Inline elements are
+ // treated as if wrapped in an anonymous block container here. See
+ // https://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level
$prev = $frame->get_prev_sibling();
- while ($prev && !in_array($prev->get_style()->display, $block_types)) {
+ while ($prev && (($prev->is_text_node() && $prev->get_node()->nodeValue === "")
+ || $prev->get_node()->nodeName === "bullet")
+ ) {
$prev = $prev->get_prev_sibling();
}
// Does the previous element allow a page break after?
- if ($prev && $prev->get_style()->page_break_after === "avoid") {
+ if ($prev && ($prev->is_block_level() || $prev->get_style()->display === "-dompdf-image")
+ && $prev->get_style()->page_break_after === "avoid"
+ ) {
Helpers::dompdf_debug("page-break", "after: avoid");
return false;
}
- // If both $prev & $frame have the same parent, check the parent's
- // page_break_inside property.
+ // Rules B & D
$parent = $frame->get_parent();
- if ($prev && $parent && $parent->get_style()->page_break_inside === "avoid") {
- Helpers::dompdf_debug("page-break", "parent inside: avoid");
+ $p = $parent;
+ while ($p) {
+ if ($p->get_style()->page_break_inside === "avoid") {
+ Helpers::dompdf_debug("page-break", "parent->inside: avoid");
- return false;
+ return false;
+ }
+ $p = $p->find_block_parent();
}
// To prevent cascading page breaks when a top-level element has
@@ -336,14 +369,11 @@ class Page extends AbstractFrameDecorator
return false;
}
- // If the frame is the first block-level frame, only allow a page
- // break if there is a (non-zero) gap between the frame and its
- // parent
- if (!$prev && $parent) {
- Helpers::dompdf_debug("page-break", "First block level frame, checking gap");
+ // Check for a possible type (3) break
+ if (!$prev && $parent && !$this->hasGap($frame->get_position("y"), $parent)) {
+ Helpers::dompdf_debug("page-break", "First block-level frame, no gap");
- return $frame->get_style()->length_in_pt($frame->get_style()->margin_top) != 0
- || $parent->get_style()->length_in_pt($parent->get_style()->padding_top) != 0;
+ return false;
}
Helpers::dompdf_debug("page-break", "block: break allowed");
@@ -352,7 +382,7 @@ class Page extends AbstractFrameDecorator
} // Inline frames (2):
else {
- if (in_array($display, Style::$INLINE_TYPES)) {
+ if ($frame->is_inline_level()) {
// Avoid breaks within table-cells
if ($this->_in_table) {
@@ -363,7 +393,18 @@ class Page extends AbstractFrameDecorator
// Rule C
$block_parent = $frame->find_block_parent();
- if (count($block_parent->get_line_boxes()) < $frame->get_style()->orphans) {
+ $parent_style = $block_parent->get_style();
+ $line = $block_parent->get_current_line_box();
+ $line_count = count($block_parent->get_line_boxes());
+ $line_number = $frame->get_containing_line() && empty($line->get_frames())
+ ? $line_count - 1
+ : $line_count;
+
+ // The line number of the frame can be less than the current
+ // number of line boxes, in case we are backtracking. As long as
+ // we are not checking for widows yet, just checking against the
+ // number of line boxes is sufficient in most cases, though.
+ if ($line_number <= $parent_style->orphans) {
Helpers::dompdf_debug("page-break", "orphans");
return false;
@@ -398,11 +439,6 @@ class Page extends AbstractFrameDecorator
return false;
}
- // Skip breaks on empty text nodes
- if ($frame->is_text_node() && $frame->get_node()->nodeValue == "") {
- return false;
- }
-
Helpers::dompdf_debug("page-break", "inline: break allowed");
return true;
@@ -410,8 +446,51 @@ class Page extends AbstractFrameDecorator
// Table-rows
} else {
if ($display === "table-row") {
- // Simply check if the parent table's page_break_inside property is
- // not 'avoid'
+
+ // If this is a nested table, prevent the page from breaking
+ if ($this->_in_table > 1) {
+ Helpers::dompdf_debug("page-break", "table: nested table");
+
+ return false;
+ }
+
+ // Rule A (table row)
+ if ($frame->get_style()->page_break_before === "avoid") {
+ Helpers::dompdf_debug("page-break", "before: avoid");
+
+ return false;
+ }
+
+ // Find the preceding row
+ $prev = $frame->get_prev_sibling();
+
+ if (!$prev) {
+ $prev_group = $frame->get_parent()->get_prev_sibling();
+
+ if ($prev_group
+ && in_array($prev_group->get_style()->display, Table::ROW_GROUPS, true)
+ ) {
+ $prev = $prev_group->get_last_child();
+ }
+ }
+
+ // Check if a page break is allowed after the preceding row
+ if ($prev && $prev->get_style()->page_break_after === "avoid") {
+ Helpers::dompdf_debug("page-break", "after: avoid");
+
+ return false;
+ }
+
+ // Avoid breaking before the first row of a table
+ if (!$prev) {
+ Helpers::dompdf_debug("page-break", "table: first-row");
+
+ return false;
+ }
+
+ // Rule B (table row)
+ // Check if the page_break_inside property is not 'avoid'
+ // for the parent table or any of its ancestors
$table = Table::find_parent_table($frame);
$p = $table;
@@ -424,38 +503,23 @@ class Page extends AbstractFrameDecorator
$p = $p->find_block_parent();
}
- // Avoid breaking before the first row of a table
- if ($table && $table->get_first_child() === $frame || $table->get_first_child()->get_first_child() === $frame) {
- Helpers::dompdf_debug("page-break", "table: first-row");
-
- return false;
- }
-
- // If this is a nested table, prevent the page from breaking
- if ($this->_in_table > 1) {
- Helpers::dompdf_debug("page-break", "table: nested table");
-
- return false;
- }
-
- Helpers::dompdf_debug("page-break", "table-row/row-groups: break allowed");
+ Helpers::dompdf_debug("page-break", "table-row: break allowed");
return true;
} else {
- if (in_array($display, Table::$ROW_GROUPS)) {
+ if (in_array($display, Table::ROW_GROUPS, true)) {
// Disallow breaks at row-groups: only split at row boundaries
return false;
} else {
- Helpers::dompdf_debug("page-break", "? " . $frame->get_style()->display . "");
+ Helpers::dompdf_debug("page-break", "? " . $display);
return false;
}
}
}
}
-
}
/**
@@ -463,13 +527,16 @@ class Page extends AbstractFrameDecorator
* the frame tree is modified so that a page break occurs in the
* correct location.
*
- * @param Frame $frame the frame to check
+ * @param AbstractFrameDecorator $frame the frame to check
*
* @return bool
*/
function check_page_break(Frame $frame)
{
- if ($this->_page_full || $frame->_already_pushed) {
+ if ($this->_page_full || $frame->_already_pushed
+ // Never check for breaks on empty text nodes
+ || ($frame->is_text_node() && $frame->get_node()->nodeValue === "")
+ ) {
return false;
}
@@ -497,13 +564,14 @@ class Page extends AbstractFrameDecorator
// If a split is to occur here, then the bottom margins & paddings of all
// parents of $frame must fit on the page as well:
$p = $frame->get_parent();
- while ($p) {
- $max_y += (float) $p->get_style()->computed_bottom_spacing();
+ while ($p && $p !== $this) {
+ $cbw = $p->get_containing_block("w");
+ $max_y += (float) $p->get_style()->computed_bottom_spacing($cbw);
$p = $p->get_parent();
}
// Check if $frame flows off the page
- if ($max_y <= $this->_bottom_page_margin) {
+ if (Helpers::lengthLessOrEqual($max_y, $this->bottom_page_edge)) {
// no: do nothing
return false;
}
@@ -557,12 +625,18 @@ class Page extends AbstractFrameDecorator
break;
}
- if ($next = $iter->get_prev_sibling()) {
+ $next = $iter->get_prev_sibling();
+ // Skip empty text nodes
+ while ($next && $next->is_text_node() && $next->get_node()->nodeValue === "") {
+ $next = $next->get_prev_sibling();
+ }
+
+ if ($next) {
Helpers::dompdf_debug("page-break", "following prev sibling.");
if ($next->is_table() && !$iter->is_table()) {
$this->_in_table++;
- } else if (!$next->is_table() && $iter->is_table()) {
+ } elseif (!$next->is_table() && $iter->is_table()) {
$this->_in_table--;
}
@@ -616,11 +690,7 @@ class Page extends AbstractFrameDecorator
//........................................................................
- /**
- * @param Frame|null $frame
- * @param bool $force_pagebreak
- */
- function split(Frame $frame = null, $force_pagebreak = false)
+ public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
{
// Do nothing
}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Table.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Table.php
new file mode 100644
index 000000000..2770cbecb
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Table.php
@@ -0,0 +1,343 @@
+_cellmap = new Cellmap($this);
+
+ if ($frame->get_style()->table_layout === "fixed") {
+ $this->_cellmap->set_layout_fixed(true);
+ }
+
+ $this->_headers = [];
+ $this->_footers = [];
+ }
+
+ public function reset()
+ {
+ parent::reset();
+ $this->_cellmap->reset();
+ $this->_headers = [];
+ $this->_footers = [];
+ $this->_reflower->reset();
+ }
+
+ //........................................................................
+
+ /**
+ * Split the table at $row. $row and all subsequent rows will be
+ * added to the clone. This method is overridden in order to remove
+ * frames from the cellmap properly.
+ */
+ public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
+ {
+ if (is_null($child)) {
+ parent::split($child, $page_break, $forced);
+ return;
+ }
+
+ // If $child is a header or if it is the first non-header row, do
+ // not duplicate headers, simply move the table to the next page.
+ if (count($this->_headers)
+ && !in_array($child, $this->_headers, true)
+ && !in_array($child->get_prev_sibling(), $this->_headers, true)
+ ) {
+ $first_header = null;
+
+ // Insert copies of the table headers before $child
+ foreach ($this->_headers as $header) {
+
+ $new_header = $header->deep_copy();
+
+ if (is_null($first_header)) {
+ $first_header = $new_header;
+ }
+
+ $this->insert_child_before($new_header, $child);
+ }
+
+ parent::split($first_header, $page_break, $forced);
+
+ } elseif (in_array($child->get_style()->display, self::ROW_GROUPS, true)) {
+
+ // Individual rows should have already been handled
+ parent::split($child, $page_break, $forced);
+
+ } else {
+
+ $iter = $child;
+
+ while ($iter) {
+ $this->_cellmap->remove_row($iter);
+ $iter = $iter->get_next_sibling();
+ }
+
+ parent::split($child, $page_break, $forced);
+ }
+ }
+
+ public function copy(DOMNode $node)
+ {
+ $deco = parent::copy($node);
+
+ // In order to keep columns' widths through pages
+ $deco->_cellmap->set_columns($this->_cellmap->get_columns());
+ $deco->_cellmap->lock_columns();
+
+ return $deco;
+ }
+
+ /**
+ * Static function to locate the parent table of a frame
+ *
+ * @param Frame $frame
+ *
+ * @return Table the table that is an ancestor of $frame
+ */
+ public static function find_parent_table(Frame $frame)
+ {
+ while ($frame = $frame->get_parent()) {
+ if ($frame->is_table()) {
+ break;
+ }
+ }
+
+ return $frame;
+ }
+
+ /**
+ * Return this table's Cellmap
+ *
+ * @return Cellmap
+ */
+ public function get_cellmap()
+ {
+ return $this->_cellmap;
+ }
+
+ //........................................................................
+
+ /**
+ * Check for text nodes between valid table children that only contain white
+ * space, except if white space is to be preserved.
+ *
+ * @param AbstractFrameDecorator $frame
+ *
+ * @return bool
+ */
+ private function isEmptyTextNode(AbstractFrameDecorator $frame): bool
+ {
+ // This is based on the white-space pattern in `FrameReflower\Text`,
+ // i.e. only match on collapsible white space
+ $wsPattern = '/^[^\S\xA0\x{202F}\x{2007}]*$/u';
+ $validChildOrNull = function ($frame) {
+ return $frame === null
+ || in_array($frame->get_style()->display, self::VALID_CHILDREN, true);
+ };
+
+ return $frame instanceof Text
+ && !$frame->is_pre()
+ && preg_match($wsPattern, $frame->get_text())
+ && $validChildOrNull($frame->get_prev_sibling())
+ && $validChildOrNull($frame->get_next_sibling());
+ }
+
+ /**
+ * Restructure tree so that the table has the correct structure. Misplaced
+ * children are appropriately wrapped in anonymous row groups, rows, and
+ * cells.
+ *
+ * https://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
+ */
+ public function normalize(): void
+ {
+ $column_caption = ["table-column-group", "table-column", "table-caption"];
+ $children = iterator_to_array($this->get_children());
+ $tbody = null;
+
+ foreach ($children as $child) {
+ $display = $child->get_style()->display;
+
+ if (in_array($display, self::ROW_GROUPS, true)) {
+ // Reset anonymous tbody
+ $tbody = null;
+
+ // Add headers and footers
+ if ($display === "table-header-group") {
+ $this->_headers[] = $child;
+ } elseif ($display === "table-footer-group") {
+ $this->_footers[] = $child;
+ }
+ continue;
+ }
+
+ if (in_array($display, $column_caption, true)) {
+ continue;
+ }
+
+ // Remove empty text nodes between valid children
+ if ($this->isEmptyTextNode($child)) {
+ $this->remove_child($child);
+ continue;
+ }
+
+ // Catch consecutive misplaced frames within a single anonymous group
+ if ($tbody === null) {
+ $tbody = $this->create_anonymous_child("tbody", "table-row-group");
+ $this->insert_child_before($tbody, $child);
+ }
+
+ $tbody->append_child($child);
+ }
+
+ // Handle empty table: Make sure there is at least one row group
+ if (!$this->get_first_child()) {
+ $tbody = $this->create_anonymous_child("tbody", "table-row-group");
+ $this->append_child($tbody);
+ }
+
+ foreach ($this->get_children() as $child) {
+ $display = $child->get_style()->display;
+
+ if (in_array($display, self::ROW_GROUPS, true)) {
+ $this->normalizeRowGroup($child);
+ }
+ }
+ }
+
+ private function normalizeRowGroup(AbstractFrameDecorator $frame): void
+ {
+ $children = iterator_to_array($frame->get_children());
+ $tr = null;
+
+ foreach ($children as $child) {
+ $display = $child->get_style()->display;
+
+ if ($display === "table-row") {
+ // Reset anonymous tr
+ $tr = null;
+ continue;
+ }
+
+ // Remove empty text nodes between valid children
+ if ($this->isEmptyTextNode($child)) {
+ $frame->remove_child($child);
+ continue;
+ }
+
+ // Catch consecutive misplaced frames within a single anonymous row
+ if ($tr === null) {
+ $tr = $frame->create_anonymous_child("tr", "table-row");
+ $frame->insert_child_before($tr, $child);
+ }
+
+ $tr->append_child($child);
+ }
+
+ // Handle empty row group: Make sure there is at least one row
+ if (!$frame->get_first_child()) {
+ $tr = $frame->create_anonymous_child("tr", "table-row");
+ $frame->append_child($tr);
+ }
+
+ foreach ($frame->get_children() as $child) {
+ $this->normalizeRow($child);
+ }
+ }
+
+ private function normalizeRow(AbstractFrameDecorator $frame): void
+ {
+ $children = iterator_to_array($frame->get_children());
+ $td = null;
+
+ foreach ($children as $child) {
+ $display = $child->get_style()->display;
+
+ if ($display === "table-cell") {
+ // Reset anonymous td
+ $td = null;
+ continue;
+ }
+
+ // Remove empty text nodes between valid children
+ if ($this->isEmptyTextNode($child)) {
+ $frame->remove_child($child);
+ continue;
+ }
+
+ // Catch consecutive misplaced frames within a single anonymous cell
+ if ($td === null) {
+ $td = $frame->create_anonymous_child("td", "table-cell");
+ $frame->insert_child_before($td, $child);
+ }
+
+ $td->append_child($child);
+ }
+
+ // Handle empty row: Make sure there is at least one cell
+ if (!$frame->get_first_child()) {
+ $td = $frame->create_anonymous_child("td", "table-cell");
+ $frame->append_child($td);
+ }
+ }
+}
diff --git a/library/vendor/dompdf/src/FrameDecorator/TableCell.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableCell.php
similarity index 92%
rename from library/vendor/dompdf/src/FrameDecorator/TableCell.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableCell.php
index 9bf77258e..d382164a3 100644
--- a/library/vendor/dompdf/src/FrameDecorator/TableCell.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableCell.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameDecorator;
@@ -32,7 +31,7 @@ class TableCell extends BlockFrameDecorator
function __construct(Frame $frame, Dompdf $dompdf)
{
parent::__construct($frame, $dompdf);
- $this->_resolved_borders = array();
+ $this->_resolved_borders = [];
$this->_content_height = 0;
}
@@ -41,7 +40,7 @@ class TableCell extends BlockFrameDecorator
function reset()
{
parent::reset();
- $this->_resolved_borders = array();
+ $this->_resolved_borders = [];
$this->_content_height = 0;
$this->_frame->reset();
}
@@ -69,19 +68,19 @@ class TableCell extends BlockFrameDecorator
{
$style = $this->get_style();
$v_space = (float)$style->length_in_pt(
- array(
+ [
$style->margin_top,
$style->padding_top,
$style->border_top_width,
$style->border_bottom_width,
$style->padding_bottom,
$style->margin_bottom
- ),
+ ],
(float)$style->length_in_pt($style->height)
);
$new_height = $height - $v_space;
- $style->height = $new_height;
+ $style->set_used("height", $new_height);
if ($new_height > $this->_content_height) {
$y_offset = 0;
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php
new file mode 100644
index 000000000..ba985c916
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php
@@ -0,0 +1,28 @@
+
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameDecorator;
@@ -32,22 +31,20 @@ class TableRowGroup extends AbstractFrameDecorator
}
/**
- * Override split() to remove all child rows and this element from the cellmap
- *
- * @param Frame $child
- * @param bool $force_pagebreak
- *
- * @return void
+ * Split the row group at the given child and remove all subsequent child
+ * rows and all subsequent row groups from the cellmap.
*/
- function split(Frame $child = null, $force_pagebreak = false)
+ public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
{
if (is_null($child)) {
- parent::split();
+ parent::split($child, $page_break, $forced);
return;
}
// Remove child & all subsequent rows from the cellmap
- $cellmap = $this->get_parent()->get_cellmap();
+ /** @var Table $parent */
+ $parent = $this->get_parent();
+ $cellmap = $parent->get_cellmap();
$iter = $child;
while ($iter) {
@@ -55,16 +52,23 @@ class TableRowGroup extends AbstractFrameDecorator
$iter = $iter->get_next_sibling();
}
+ // Remove all subsequent row groups from the cellmap
+ $iter = $this->get_next_sibling();
+
+ while ($iter) {
+ $cellmap->remove_row_group($iter);
+ $iter = $iter->get_next_sibling();
+ }
+
// If we are splitting at the first child remove the
// table-row-group from the cellmap as well
if ($child === $this->get_first_child()) {
$cellmap->remove_row_group($this);
- parent::split();
+ parent::split(null, $page_break, $forced);
return;
}
$cellmap->update_row_group($this, $child->get_prev_sibling());
- parent::split($child);
+ parent::split($child, $page_break, $forced);
}
}
-
diff --git a/library/vendor/dompdf/src/FrameDecorator/Text.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Text.php
similarity index 58%
rename from library/vendor/dompdf/src/FrameDecorator/Text.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Text.php
index b1c1ea7a0..9d6693464 100644
--- a/library/vendor/dompdf/src/FrameDecorator/Text.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Text.php
@@ -1,10 +1,7 @@
- * @author Brian Sweeney
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameDecorator;
@@ -16,14 +13,14 @@ use Dompdf\Exception;
/**
* Decorates Frame objects for text layout
*
- * @access private
* @package dompdf
*/
class Text extends AbstractFrameDecorator
{
-
- // protected members
- protected $_text_spacing;
+ /**
+ * @var float
+ */
+ protected $text_spacing;
/**
* Text constructor.
@@ -38,23 +35,23 @@ class Text extends AbstractFrameDecorator
}
parent::__construct($frame, $dompdf);
- $this->_text_spacing = null;
+ $this->text_spacing = 0.0;
}
function reset()
{
parent::reset();
- $this->_text_spacing = null;
+ $this->text_spacing = 0.0;
}
// Accessor methods
/**
- * @return null
+ * @return float
*/
- function get_text_spacing()
+ public function get_text_spacing(): float
{
- return $this->_text_spacing;
+ return $this->text_spacing;
}
/**
@@ -82,86 +79,74 @@ class Text extends AbstractFrameDecorator
//........................................................................
/**
- * Vertical margins & padding do not apply to text frames
+ * Vertical padding, border, and margin do not apply when determining the
+ * height for inline frames.
*
- * http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced:
+ * http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced
*
* The vertical padding, border and margin of an inline, non-replaced box
* start at the top and bottom of the content area, not the
* 'line-height'. But only the 'line-height' is used to calculate the
* height of the line box.
*
- * @return float|int
+ * @return float
*/
- function get_margin_height()
+ public function get_margin_height(): float
{
- // This function is called in add_frame_to_line() and is used to
- // determine the line height, so we actually want to return the
- // 'line-height' property, not the actual margin box
- $style = $this->get_parent()->get_style();
+ // This function is also called in add_frame_to_line() and is used to
+ // determine the line height
+ $style = $this->get_style();
$font = $style->font_family;
$size = $style->font_size;
+ $fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size);
- /*
- Helpers::pre_r('-----');
- Helpers::pre_r($style->line_height);
- Helpers::pre_r($style->font_size);
- Helpers::pre_r($this->_dompdf->getFontMetrics()->getFontHeight($font, $size));
- Helpers::pre_r(($style->line_height / $size) * $this->_dompdf->getFontMetrics()->getFontHeight($font, $size));
- */
-
- return ($style->line_height / ($size > 0 ? $size : 1)) * $this->_dompdf->getFontMetrics()->getFontHeight($font, $size);
+ return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight;
}
- /**
- * @return array
- */
- function get_padding_box()
+ public function get_padding_box(): array
{
+ $style = $this->_frame->get_style();
$pb = $this->_frame->get_padding_box();
- $pb[3] = $pb["h"] = $this->_frame->get_style()->height;
-
+ $pb[3] = $pb["h"] = (float) $style->length_in_pt($style->height);
return $pb;
}
/**
- * @param $spacing
+ * @param float $spacing
*/
- function set_text_spacing($spacing)
+ public function set_text_spacing(float $spacing): void
{
- $style = $this->_frame->get_style();
-
- $this->_text_spacing = $spacing;
- $char_spacing = (float)$style->length_in_pt($style->letter_spacing);
-
- // Re-adjust our width to account for the change in spacing
- $style->width = $this->_dompdf->getFontMetrics()->getTextWidth($this->get_text(), $style->font_family, $style->font_size, $spacing, $char_spacing);
+ $this->text_spacing = $spacing;
+ $this->recalculate_width();
}
/**
- * Recalculate the text width
+ * Recalculate the text width
*
* @return float
*/
- function recalculate_width()
+ public function recalculate_width(): float
{
+ $fontMetrics = $this->_dompdf->getFontMetrics();
$style = $this->get_style();
$text = $this->get_text();
- $size = $style->font_size;
$font = $style->font_family;
- $word_spacing = (float)$style->length_in_pt($style->word_spacing);
- $char_spacing = (float)$style->length_in_pt($style->letter_spacing);
+ $size = $style->font_size;
+ $word_spacing = $this->text_spacing + $style->word_spacing;
+ $letter_spacing = $style->letter_spacing;
+ $text_width = $fontMetrics->getTextWidth($text, $font, $size, $word_spacing, $letter_spacing);
- return $style->width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $char_spacing);
+ $style->set_used("width", $text_width);
+ return $text_width;
}
// Text manipulation methods
/**
- * split the text in this frame at the offset specified. The remaining
- * text is added a sibling frame following this one and is returned.
+ * Split the text in this frame at the offset specified. The remaining
+ * text is added as a sibling frame following this one and is returned.
*
- * @param $offset
+ * @param int $offset
* @return Frame|null
*/
function split_text($offset)
@@ -171,7 +156,10 @@ class Text extends AbstractFrameDecorator
}
$split = $this->_frame->get_node()->splitText($offset);
-
+ if ($split === false) {
+ return null;
+ }
+
$deco = $this->copy($split);
$p = $this->get_parent();
@@ -185,8 +173,8 @@ class Text extends AbstractFrameDecorator
}
/**
- * @param $offset
- * @param $count
+ * @param int $offset
+ * @param int $count
*/
function delete_text($offset, $count)
{
@@ -194,7 +182,7 @@ class Text extends AbstractFrameDecorator
}
/**
- * @param $text
+ * @param string $text
*/
function set_text($text)
{
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php
new file mode 100644
index 000000000..fa0cc511b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php
@@ -0,0 +1,705 @@
+_frame = $frame;
+ $this->_min_max_child_cache = null;
+ $this->_min_max_cache = null;
+ }
+
+ /**
+ * @return Dompdf
+ */
+ function get_dompdf()
+ {
+ return $this->_frame->get_dompdf();
+ }
+
+ public function reset(): void
+ {
+ $this->_min_max_child_cache = null;
+ $this->_min_max_cache = null;
+ }
+
+ /**
+ * Determine the actual containing block for absolute and fixed position.
+ *
+ * https://www.w3.org/TR/CSS21/visudet.html#containing-block-details
+ */
+ protected function determine_absolute_containing_block(): void
+ {
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+
+ switch ($style->position) {
+ case "absolute":
+ $parent = $frame->find_positioned_parent();
+ if ($parent !== $frame->get_root()) {
+ $parent_style = $parent->get_style();
+ $parent_padding_box = $parent->get_padding_box();
+ //FIXME: an accurate measure of the positioned parent height
+ // is not possible until reflow has completed;
+ // we'll fall back to the parent's containing block,
+ // which is wrong for auto-height parents
+ if ($parent_style->height === "auto") {
+ $parent_containing_block = $parent->get_containing_block();
+ $containing_block_height = $parent_containing_block["h"] -
+ (float)$parent_style->length_in_pt([
+ $parent_style->margin_top,
+ $parent_style->margin_bottom,
+ $parent_style->border_top_width,
+ $parent_style->border_bottom_width
+ ], $parent_containing_block["w"]);
+ } else {
+ $containing_block_height = $parent_padding_box["h"];
+ }
+ $frame->set_containing_block($parent_padding_box["x"], $parent_padding_box["y"], $parent_padding_box["w"], $containing_block_height);
+ break;
+ }
+ case "fixed":
+ $initial_cb = $frame->get_root()->get_first_child()->get_containing_block();
+ $frame->set_containing_block($initial_cb["x"], $initial_cb["y"], $initial_cb["w"], $initial_cb["h"]);
+ break;
+ default:
+ // Nothing to do, containing block already set via parent
+ break;
+ }
+ }
+
+ /**
+ * Collapse frames margins
+ * http://www.w3.org/TR/CSS21/box.html#collapsing-margins
+ */
+ protected function _collapse_margins(): void
+ {
+ $frame = $this->_frame;
+
+ // Margins of float/absolutely positioned/inline-level elements do not collapse
+ if (!$frame->is_in_flow() || $frame->is_inline_level()
+ || $frame->get_root() === $frame || $frame->get_parent() === $frame->get_root()
+ ) {
+ return;
+ }
+
+ $cb = $frame->get_containing_block();
+ $style = $frame->get_style();
+
+ $t = $style->length_in_pt($style->margin_top, $cb["w"]);
+ $b = $style->length_in_pt($style->margin_bottom, $cb["w"]);
+
+ // Handle 'auto' values
+ if ($t === "auto") {
+ $style->set_used("margin_top", 0.0);
+ $t = 0.0;
+ }
+
+ if ($b === "auto") {
+ $style->set_used("margin_bottom", 0.0);
+ $b = 0.0;
+ }
+
+ // Collapse vertical margins:
+ $n = $frame->get_next_sibling();
+ if ( $n && !($n->is_block_level() && $n->is_in_flow()) ) {
+ while ($n = $n->get_next_sibling()) {
+ if ($n->is_block_level() && $n->is_in_flow()) {
+ break;
+ }
+
+ if (!$n->get_first_child()) {
+ $n = null;
+ break;
+ }
+ }
+ }
+
+ if ($n) {
+ $n_style = $n->get_style();
+ $n_t = (float)$n_style->length_in_pt($n_style->margin_top, $cb["w"]);
+
+ $b = $this->get_collapsed_margin_length($b, $n_t);
+ $style->set_used("margin_bottom", $b);
+ $n_style->set_used("margin_top", 0.0);
+ }
+
+ // Collapse our first child's margin, if there is no border or padding
+ if ($style->border_top_width == 0 && $style->length_in_pt($style->padding_top) == 0) {
+ $f = $this->_frame->get_first_child();
+ if ( $f && !($f->is_block_level() && $f->is_in_flow()) ) {
+ while ($f = $f->get_next_sibling()) {
+ if ($f->is_block_level() && $f->is_in_flow()) {
+ break;
+ }
+
+ if (!$f->get_first_child()) {
+ $f = null;
+ break;
+ }
+ }
+ }
+
+ // Margins are collapsed only between block-level boxes
+ if ($f) {
+ $f_style = $f->get_style();
+ $f_t = (float)$f_style->length_in_pt($f_style->margin_top, $cb["w"]);
+
+ $t = $this->get_collapsed_margin_length($t, $f_t);
+ $style->set_used("margin_top", $t);
+ $f_style->set_used("margin_top", 0.0);
+ }
+ }
+
+ // Collapse our last child's margin, if there is no border or padding
+ if ($style->border_bottom_width == 0 && $style->length_in_pt($style->padding_bottom) == 0) {
+ $l = $this->_frame->get_last_child();
+ if ( $l && !($l->is_block_level() && $l->is_in_flow()) ) {
+ while ($l = $l->get_prev_sibling()) {
+ if ($l->is_block_level() && $l->is_in_flow()) {
+ break;
+ }
+
+ if (!$l->get_last_child()) {
+ $l = null;
+ break;
+ }
+ }
+ }
+
+ // Margins are collapsed only between block-level boxes
+ if ($l) {
+ $l_style = $l->get_style();
+ $l_b = (float)$l_style->length_in_pt($l_style->margin_bottom, $cb["w"]);
+
+ $b = $this->get_collapsed_margin_length($b, $l_b);
+ $style->set_used("margin_bottom", $b);
+ $l_style->set_used("margin_bottom", 0.0);
+ }
+ }
+ }
+
+ /**
+ * Get the combined (collapsed) length of two adjoining margins.
+ *
+ * See http://www.w3.org/TR/CSS21/box.html#collapsing-margins.
+ *
+ * @param float $l1
+ * @param float $l2
+ *
+ * @return float
+ */
+ private function get_collapsed_margin_length(float $l1, float $l2): float
+ {
+ if ($l1 < 0 && $l2 < 0) {
+ return min($l1, $l2); // min(x, y) = - max(abs(x), abs(y)), if x < 0 && y < 0
+ }
+
+ if ($l1 < 0 || $l2 < 0) {
+ return $l1 + $l2; // x + y = x - abs(y), if y < 0
+ }
+
+ return max($l1, $l2);
+ }
+
+ /**
+ * Handle relative positioning according to
+ * https://www.w3.org/TR/CSS21/visuren.html#relative-positioning.
+ *
+ * @param AbstractFrameDecorator $frame The frame to handle.
+ */
+ protected function position_relative(AbstractFrameDecorator $frame): void
+ {
+ $style = $frame->get_style();
+
+ if ($style->position === "relative") {
+ $cb = $frame->get_containing_block();
+ $top = $style->length_in_pt($style->top, $cb["h"]);
+ $right = $style->length_in_pt($style->right, $cb["w"]);
+ $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
+ $left = $style->length_in_pt($style->left, $cb["w"]);
+
+ // FIXME RTL case:
+ // if ($left !== "auto" && $right !== "auto") $left = -$right;
+ if ($left === "auto" && $right === "auto") {
+ $left = 0;
+ } elseif ($left === "auto") {
+ $left = -$right;
+ }
+
+ if ($top === "auto" && $bottom === "auto") {
+ $top = 0;
+ } elseif ($top === "auto") {
+ $top = -$bottom;
+ }
+
+ $frame->move($left, $top);
+ }
+ }
+
+ /**
+ * @param Block|null $block
+ */
+ abstract function reflow(Block $block = null);
+
+ /**
+ * Resolve the `min-width` property.
+ *
+ * Resolves to 0 if not set or if a percentage and the containing-block
+ * width is not defined.
+ *
+ * @param float|null $cbw Width of the containing block.
+ *
+ * @return float
+ */
+ protected function resolve_min_width(?float $cbw): float
+ {
+ $style = $this->_frame->get_style();
+ $min_width = $style->min_width;
+
+ return $min_width !== "auto"
+ ? $style->length_in_pt($min_width, $cbw ?? 0)
+ : 0.0;
+ }
+
+ /**
+ * Resolve the `max-width` property.
+ *
+ * Resolves to `INF` if not set or if a percentage and the containing-block
+ * width is not defined.
+ *
+ * @param float|null $cbw Width of the containing block.
+ *
+ * @return float
+ */
+ protected function resolve_max_width(?float $cbw): float
+ {
+ $style = $this->_frame->get_style();
+ $max_width = $style->max_width;
+
+ return $max_width !== "none"
+ ? $style->length_in_pt($max_width, $cbw ?? INF)
+ : INF;
+ }
+
+ /**
+ * Resolve the `min-height` property.
+ *
+ * Resolves to 0 if not set or if a percentage and the containing-block
+ * height is not defined.
+ *
+ * @param float|null $cbh Height of the containing block.
+ *
+ * @return float
+ */
+ protected function resolve_min_height(?float $cbh): float
+ {
+ $style = $this->_frame->get_style();
+ $min_height = $style->min_height;
+
+ return $min_height !== "auto"
+ ? $style->length_in_pt($min_height, $cbh ?? 0)
+ : 0.0;
+ }
+
+ /**
+ * Resolve the `max-height` property.
+ *
+ * Resolves to `INF` if not set or if a percentage and the containing-block
+ * height is not defined.
+ *
+ * @param float|null $cbh Height of the containing block.
+ *
+ * @return float
+ */
+ protected function resolve_max_height(?float $cbh): float
+ {
+ $style = $this->_frame->get_style();
+ $max_height = $style->max_height;
+
+ return $max_height !== "none"
+ ? $style->length_in_pt($style->max_height, $cbh ?? INF)
+ : INF;
+ }
+
+ /**
+ * Get the minimum and maximum preferred width of the contents of the frame,
+ * as requested by its children.
+ *
+ * @return array A two-element array of min and max width.
+ */
+ public function get_min_max_child_width(): array
+ {
+ if (!is_null($this->_min_max_child_cache)) {
+ return $this->_min_max_child_cache;
+ }
+
+ $low = [];
+ $high = [];
+
+ for ($iter = $this->_frame->get_children(); $iter->valid(); $iter->next()) {
+ $inline_min = 0;
+ $inline_max = 0;
+
+ // Add all adjacent inline widths together to calculate max width
+ while ($iter->valid() && ($iter->current()->is_inline_level() || $iter->current()->get_style()->display === "-dompdf-image")) {
+ /** @var AbstractFrameDecorator */
+ $child = $iter->current();
+ $child->get_reflower()->_set_content();
+ $minmax = $child->get_min_max_width();
+
+ if (in_array($child->get_style()->white_space, ["pre", "nowrap"], true)) {
+ $inline_min += $minmax["min"];
+ } else {
+ $low[] = $minmax["min"];
+ }
+
+ $inline_max += $minmax["max"];
+ $iter->next();
+ }
+
+ if ($inline_min > 0) {
+ $low[] = $inline_min;
+ }
+ if ($inline_max > 0) {
+ $high[] = $inline_max;
+ }
+
+ // Skip children with absolute position
+ if ($iter->valid() && !$iter->current()->is_absolute()) {
+ /** @var AbstractFrameDecorator */
+ $child = $iter->current();
+ $child->get_reflower()->_set_content();
+ list($low[], $high[]) = $child->get_min_max_width();
+ }
+ }
+
+ $min = count($low) ? max($low) : 0;
+ $max = count($high) ? max($high) : 0;
+
+ return $this->_min_max_child_cache = [$min, $max];
+ }
+
+ /**
+ * Get the minimum and maximum preferred content-box width of the frame.
+ *
+ * @return array A two-element array of min and max width.
+ */
+ public function get_min_max_content_width(): array
+ {
+ return $this->get_min_max_child_width();
+ }
+
+ /**
+ * Get the minimum and maximum preferred border-box width of the frame.
+ *
+ * Required for shrink-to-fit width calculation, as used in automatic table
+ * layout, absolute positioning, float and inline-block. This provides a
+ * basic implementation. Child classes should override this or
+ * `get_min_max_content_width` as necessary.
+ *
+ * @return array An array `[0 => min, 1 => max, "min" => min, "max" => max]`
+ * of min and max width.
+ */
+ public function get_min_max_width(): array
+ {
+ if (!is_null($this->_min_max_cache)) {
+ return $this->_min_max_cache;
+ }
+
+ $style = $this->_frame->get_style();
+ [$min, $max] = $this->get_min_max_content_width();
+
+ // Account for margins, borders, and padding
+ $dims = [
+ $style->padding_left,
+ $style->padding_right,
+ $style->border_left_width,
+ $style->border_right_width,
+ $style->margin_left,
+ $style->margin_right
+ ];
+
+ // The containing block is not defined yet, treat percentages as 0
+ $delta = (float) $style->length_in_pt($dims, 0);
+ $min += $delta;
+ $max += $delta;
+
+ return $this->_min_max_cache = [$min, $max, "min" => $min, "max" => $max];
+ }
+
+ /**
+ * Parses a CSS string containing quotes and escaped hex characters
+ *
+ * @param $string string The CSS string to parse
+ * @param $single_trim
+ * @return string
+ */
+ protected function _parse_string($string, $single_trim = false)
+ {
+ if ($single_trim) {
+ $string = preg_replace('/^[\"\']/', "", $string);
+ $string = preg_replace('/[\"\']$/', "", $string);
+ } else {
+ $string = trim($string, "'\"");
+ }
+
+ $string = str_replace(["\\\n", '\\"', "\\'"],
+ ["", '"', "'"], $string);
+
+ // Convert escaped hex characters into ascii characters (e.g. \A => newline)
+ $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/",
+ function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); },
+ $string);
+ return $string;
+ }
+
+ /**
+ * Parses a CSS "quotes" property
+ *
+ * https://www.w3.org/TR/css-content-3/#quotes
+ *
+ * @return array An array of pairs of quotes
+ */
+ protected function _parse_quotes(): array
+ {
+ $quotes = $this->_frame->get_style()->quotes;
+
+ if ($quotes === "none") {
+ return [];
+ }
+
+ if ($quotes === "auto") {
+ // TODO: Use typographically appropriate quotes for the current
+ // language here
+ return [['"', '"'], ["'", "'"]];
+ }
+
+ // Matches quote types
+ $re = '/(\'[^\']*\')|(\"[^\"]*\")/';
+
+ // Split on spaces, except within quotes
+ if (!preg_match_all($re, $quotes, $matches, PREG_SET_ORDER)) {
+ return [];
+ }
+
+ $quotes_array = [];
+ foreach ($matches as $_quote) {
+ $quotes_array[] = $this->_parse_string($_quote[0], true);
+ }
+
+ return array_chunk($quotes_array, 2);
+ }
+
+ /**
+ * Parses the CSS "content" property
+ *
+ * https://www.w3.org/TR/CSS21/generate.html#content
+ *
+ * @return string The resulting string
+ */
+ protected function _parse_content(): string
+ {
+ $style = $this->_frame->get_style();
+ $content = $style->content;
+
+ if ($content === "normal" || $content === "none") {
+ return "";
+ }
+
+ $quotes = $this->_parse_quotes();
+ $text = "";
+
+ foreach ($content as $val) {
+ // String
+ if (in_array(mb_substr($val, 0, 1), ['"', "'"], true)) {
+ $text .= $this->_parse_string($val);
+ continue;
+ }
+
+ $val = mb_strtolower($val);
+
+ // Keywords
+ if ($val === "open-quote") {
+ // FIXME: Take quotation depth into account
+ if (isset($quotes[0][0])) {
+ $text .= $quotes[0][0];
+ }
+ continue;
+ } elseif ($val === "close-quote") {
+ // FIXME: Take quotation depth into account
+ if (isset($quotes[0][1])) {
+ $text .= $quotes[0][1];
+ }
+ continue;
+ } elseif ($val === "no-open-quote") {
+ // FIXME: Increment quotation depth
+ continue;
+ } elseif ($val === "no-close-quote") {
+ // FIXME: Decrement quotation depth
+ continue;
+ }
+
+ // attr()
+ if (mb_substr($val, 0, 5) === "attr(") {
+ $i = mb_strpos($val, ")");
+ if ($i === false) {
+ continue;
+ }
+
+ $attr = trim(mb_substr($val, 5, $i - 5));
+ if ($attr === "") {
+ continue;
+ }
+
+ $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
+ continue;
+ }
+
+ // counter()/counters()
+ if (mb_substr($val, 0, 7) === "counter") {
+ // Handle counter() references:
+ // http://www.w3.org/TR/CSS21/generate.html#content
+
+ $i = mb_strpos($val, ")");
+ if ($i === false) {
+ continue;
+ }
+
+ preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]*)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $val, $args);
+ $counter_id = $args[3];
+
+ if (strtolower($args[1]) === "counter") {
+ // counter(name [,style])
+ if (isset($args[5])) {
+ $type = trim($args[5]);
+ } else {
+ $type = "decimal";
+ }
+ $p = $this->_frame->lookup_counter_frame($counter_id);
+
+ $text .= $p->counter_value($counter_id, $type);
+ } elseif (strtolower($args[1]) === "counters") {
+ // counters(name, string [,style])
+ if (isset($args[5])) {
+ $string = $this->_parse_string($args[5]);
+ } else {
+ $string = "";
+ }
+
+ if (isset($args[7])) {
+ $type = trim($args[7]);
+ } else {
+ $type = "decimal";
+ }
+
+ $p = $this->_frame->lookup_counter_frame($counter_id);
+ $tmp = [];
+ while ($p) {
+ // We only want to use the counter values when they actually increment the counter
+ if (array_key_exists($counter_id, $p->_counters)) {
+ array_unshift($tmp, $p->counter_value($counter_id, $type));
+ }
+ $p = $p->lookup_counter_frame($counter_id);
+ }
+ $text .= implode($string, $tmp);
+ } else {
+ // countertops?
+ }
+
+ continue;
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * Handle counters and set generated content if the frame is a
+ * generated-content frame.
+ */
+ protected function _set_content(): void
+ {
+ $frame = $this->_frame;
+
+ if ($frame->content_set) {
+ return;
+ }
+
+ $style = $frame->get_style();
+
+ if (($reset = $style->counter_reset) !== "none") {
+ $frame->reset_counters($reset);
+ }
+
+ if (($increment = $style->counter_increment) !== "none") {
+ $frame->increment_counters($increment);
+ }
+
+ if ($frame->get_node()->nodeName === "dompdf_generated") {
+ $content = $this->_parse_content();
+
+ if ($content !== "") {
+ $node = $frame->get_node()->ownerDocument->createTextNode($content);
+
+ $new_style = $style->get_stylesheet()->create_style();
+ $new_style->inherit($style);
+
+ $new_frame = new Frame($node);
+ $new_frame->set_style($new_style);
+
+ Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root());
+ $frame->append_child($new_frame);
+ }
+ }
+
+ $frame->content_set = true;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Block.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Block.php
new file mode 100644
index 000000000..f9e50a678
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Block.php
@@ -0,0 +1,949 @@
+_frame;
+ $style = $frame->get_style();
+ $absolute = $frame->is_absolute();
+
+ $cb = $frame->get_containing_block();
+ $w = $cb["w"];
+
+ $rm = $style->length_in_pt($style->margin_right, $w);
+ $lm = $style->length_in_pt($style->margin_left, $w);
+
+ $left = $style->length_in_pt($style->left, $w);
+ $right = $style->length_in_pt($style->right, $w);
+
+ // Handle 'auto' values
+ $dims = [$style->border_left_width,
+ $style->border_right_width,
+ $style->padding_left,
+ $style->padding_right,
+ $width !== "auto" ? $width : 0,
+ $rm !== "auto" ? $rm : 0,
+ $lm !== "auto" ? $lm : 0];
+
+ // absolutely positioned boxes take the 'left' and 'right' properties into account
+ if ($absolute) {
+ $dims[] = $left !== "auto" ? $left : 0;
+ $dims[] = $right !== "auto" ? $right : 0;
+ }
+
+ $sum = (float)$style->length_in_pt($dims, $w);
+
+ // Compare to the containing block
+ $diff = $w - $sum;
+
+ if ($absolute) {
+ // Absolutely positioned
+ // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+
+ if ($width === "auto" || $left === "auto" || $right === "auto") {
+ // "all of the three are 'auto'" logic + otherwise case
+ if ($lm === "auto") {
+ $lm = 0;
+ }
+ if ($rm === "auto") {
+ $rm = 0;
+ }
+
+ $block_parent = $frame->find_block_parent();
+ $parent_content = $block_parent->get_content_box();
+ $line = $block_parent->get_current_line_box();
+
+ // TODO: This is the in-flow inline position. Use the in-flow
+ // block position if the original display type is block-level
+ $inflow_x = $parent_content["x"] - $cb["x"] + $line->left + $line->w;
+
+ if ($width === "auto" && $left === "auto" && $right === "auto") {
+ // rule 3, per instruction preceding rule set
+ // shrink-to-fit width
+ $left = $inflow_x;
+ [$min, $max] = $this->get_min_max_child_width();
+ $width = min(max($min, $diff - $left), $max);
+ $right = $diff - $left - $width;
+ } elseif ($width === "auto" && $left === "auto") {
+ // rule 1
+ // shrink-to-fit width
+ [$min, $max] = $this->get_min_max_child_width();
+ $width = min(max($min, $diff), $max);
+ $left = $diff - $width;
+ } elseif ($width === "auto" && $right === "auto") {
+ // rule 3
+ // shrink-to-fit width
+ [$min, $max] = $this->get_min_max_child_width();
+ $width = min(max($min, $diff), $max);
+ $right = $diff - $width;
+ } elseif ($left === "auto" && $right === "auto") {
+ // rule 2
+ $left = $inflow_x;
+ $right = $diff - $left;
+ } elseif ($left === "auto") {
+ // rule 4
+ $left = $diff;
+ } elseif ($width === "auto") {
+ // rule 5
+ $width = max($diff, 0);
+ } else {
+ // $right === "auto"
+ // rule 6
+ $right = $diff;
+ }
+ } else {
+ // "none of the three are 'auto'" logic described in paragraph preceding the rules
+ if ($diff >= 0) {
+ if ($lm === "auto" && $rm === "auto") {
+ $lm = $rm = $diff / 2;
+ } elseif ($lm === "auto") {
+ $lm = $diff;
+ } elseif ($rm === "auto") {
+ $rm = $diff;
+ }
+ } else {
+ // over-constrained, solve for right
+ $right = $right + $diff;
+
+ if ($lm === "auto") {
+ $lm = 0;
+ }
+ if ($rm === "auto") {
+ $rm = 0;
+ }
+ }
+ }
+ } elseif ($style->float !== "none" || $style->display === "inline-block") {
+ // Shrink-to-fit width for float and inline block
+ // https://www.w3.org/TR/CSS21/visudet.html#float-width
+ // https://www.w3.org/TR/CSS21/visudet.html#inlineblock-width
+
+ if ($width === "auto") {
+ [$min, $max] = $this->get_min_max_child_width();
+ $width = min(max($min, $diff), $max);
+ }
+ if ($lm === "auto") {
+ $lm = 0;
+ }
+ if ($rm === "auto") {
+ $rm = 0;
+ }
+ } else {
+ // Block-level, normal flow
+ // https://www.w3.org/TR/CSS21/visudet.html#blockwidth
+
+ if ($diff >= 0) {
+ // Find auto properties and get them to take up the slack
+ if ($width === "auto") {
+ $width = $diff;
+
+ if ($lm === "auto") {
+ $lm = 0;
+ }
+ if ($rm === "auto") {
+ $rm = 0;
+ }
+ } elseif ($lm === "auto" && $rm === "auto") {
+ $lm = $rm = $diff / 2;
+ } elseif ($lm === "auto") {
+ $lm = $diff;
+ } elseif ($rm === "auto") {
+ $rm = $diff;
+ }
+ } else {
+ // We are over constrained--set margin-right to the difference
+ $rm = (float) $rm + $diff;
+
+ if ($width === "auto") {
+ $width = 0;
+ }
+ if ($lm === "auto") {
+ $lm = 0;
+ }
+ }
+ }
+
+ return [
+ "width" => $width,
+ "margin_left" => $lm,
+ "margin_right" => $rm,
+ "left" => $left,
+ "right" => $right,
+ ];
+ }
+
+ /**
+ * Call the above function, but resolve max/min widths
+ *
+ * @throws Exception
+ * @return array
+ */
+ protected function _calculate_restricted_width()
+ {
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+ $cb = $frame->get_containing_block();
+
+ if (!isset($cb["w"])) {
+ throw new Exception("Box property calculation requires containing block width");
+ }
+
+ $width = $style->length_in_pt($style->width, $cb["w"]);
+
+ $values = $this->_calculate_width($width);
+ $margin_left = $values["margin_left"];
+ $margin_right = $values["margin_right"];
+ $width = $values["width"];
+ $left = $values["left"];
+ $right = $values["right"];
+
+ // Handle min/max width
+ // https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
+ $min_width = $this->resolve_min_width($cb["w"]);
+ $max_width = $this->resolve_max_width($cb["w"]);
+
+ if ($width > $max_width) {
+ $values = $this->_calculate_width($max_width);
+ $margin_left = $values["margin_left"];
+ $margin_right = $values["margin_right"];
+ $width = $values["width"];
+ $left = $values["left"];
+ $right = $values["right"];
+ }
+
+ if ($width < $min_width) {
+ $values = $this->_calculate_width($min_width);
+ $margin_left = $values["margin_left"];
+ $margin_right = $values["margin_right"];
+ $width = $values["width"];
+ $left = $values["left"];
+ $right = $values["right"];
+ }
+
+ return [$width, $margin_left, $margin_right, $left, $right];
+ }
+
+ /**
+ * Determine the unrestricted height of content within the block
+ * not by adding each line's height, but by getting the last line's position.
+ * This because lines could have been pushed lower by a clearing element.
+ *
+ * @return float
+ */
+ protected function _calculate_content_height()
+ {
+ $height = 0;
+ $lines = $this->_frame->get_line_boxes();
+ if (count($lines) > 0) {
+ $last_line = end($lines);
+ $content_box = $this->_frame->get_content_box();
+ $height = $last_line->y + $last_line->h - $content_box["y"];
+ }
+ return $height;
+ }
+
+ /**
+ * Determine the frame's restricted height
+ *
+ * @return array
+ */
+ protected function _calculate_restricted_height()
+ {
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+ $content_height = $this->_calculate_content_height();
+ $cb = $frame->get_containing_block();
+
+ $height = $style->length_in_pt($style->height, $cb["h"]);
+ $margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
+ $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
+
+ $top = $style->length_in_pt($style->top, $cb["h"]);
+ $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
+
+ if ($frame->is_absolute()) {
+ // Absolutely positioned
+ // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
+
+ $h_dims = [
+ $top !== "auto" ? $top : 0,
+ $height !== "auto" ? $height : 0,
+ $bottom !== "auto" ? $bottom : 0
+ ];
+ $w_dims = [
+ $style->margin_top !== "auto" ? $style->margin_top : 0,
+ $style->padding_top,
+ $style->border_top_width,
+ $style->border_bottom_width,
+ $style->padding_bottom,
+ $style->margin_bottom !== "auto" ? $style->margin_bottom : 0
+ ];
+
+ $sum = (float)$style->length_in_pt($h_dims, $cb["h"])
+ + (float)$style->length_in_pt($w_dims, $cb["w"]);
+
+ $diff = $cb["h"] - $sum;
+
+ if ($height === "auto" || $top === "auto" || $bottom === "auto") {
+ // "all of the three are 'auto'" logic + otherwise case
+ if ($margin_top === "auto") {
+ $margin_top = 0;
+ }
+ if ($margin_bottom === "auto") {
+ $margin_bottom = 0;
+ }
+
+ $block_parent = $frame->find_block_parent();
+ $current_line = $block_parent->get_current_line_box();
+
+ // TODO: This is the in-flow inline position. Use the in-flow
+ // block position if the original display type is block-level
+ $inflow_y = $current_line->y - $cb["y"];
+
+ if ($height === "auto" && $top === "auto" && $bottom === "auto") {
+ // rule 3, per instruction preceding rule set
+ $top = $inflow_y;
+ $height = $content_height;
+ $bottom = $diff - $top - $height;
+ } elseif ($height === "auto" && $top === "auto") {
+ // rule 1
+ $height = $content_height;
+ $top = $diff - $height;
+ } elseif ($height === "auto" && $bottom === "auto") {
+ // rule 3
+ $height = $content_height;
+ $bottom = $diff - $height;
+ } elseif ($top === "auto" && $bottom === "auto") {
+ // rule 2
+ $top = $inflow_y;
+ $bottom = $diff - $top;
+ } elseif ($top === "auto") {
+ // rule 4
+ $top = $diff;
+ } elseif ($height === "auto") {
+ // rule 5
+ $height = max($diff, 0);
+ } else {
+ // $bottom === "auto"
+ // rule 6
+ $bottom = $diff;
+ }
+ } else {
+ // "none of the three are 'auto'" logic described in paragraph preceding the rules
+ if ($diff >= 0) {
+ if ($margin_top === "auto" && $margin_bottom === "auto") {
+ $margin_top = $margin_bottom = $diff / 2;
+ } elseif ($margin_top === "auto") {
+ $margin_top = $diff;
+ } elseif ($margin_bottom === "auto") {
+ $margin_bottom = $diff;
+ }
+ } else {
+ // over-constrained, solve for bottom
+ $bottom = $bottom + $diff;
+
+ if ($margin_top === "auto") {
+ $margin_top = 0;
+ }
+ if ($margin_bottom === "auto") {
+ $margin_bottom = 0;
+ }
+ }
+ }
+ } else {
+ // https://www.w3.org/TR/CSS21/visudet.html#normal-block
+ // https://www.w3.org/TR/CSS21/visudet.html#block-root-margin
+
+ if ($height === "auto") {
+ $height = $content_height;
+ }
+ if ($margin_top === "auto") {
+ $margin_top = 0;
+ }
+ if ($margin_bottom === "auto") {
+ $margin_bottom = 0;
+ }
+
+ // Handle min/max height
+ // https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
+ $min_height = $this->resolve_min_height($cb["h"]);
+ $max_height = $this->resolve_max_height($cb["h"]);
+ $height = Helpers::clamp($height, $min_height, $max_height);
+ }
+
+ // TODO: Need to also take min/max height into account for absolute
+ // positioning, using similar logic to the `_calculate_width`/
+ // `calculate_restricted_width` split above. The non-absolute case
+ // can simply clamp height within min/max, as margins and offsets are
+ // not affected
+
+ return [$height, $margin_top, $margin_bottom, $top, $bottom];
+ }
+
+ /**
+ * Adjust the justification of each of our lines.
+ * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
+ */
+ protected function _text_align()
+ {
+ $style = $this->_frame->get_style();
+ $w = $this->_frame->get_containing_block("w");
+ $width = (float)$style->length_in_pt($style->width, $w);
+ $text_indent = (float)$style->length_in_pt($style->text_indent, $w);
+
+ switch ($style->text_align) {
+ default:
+ case "left":
+ foreach ($this->_frame->get_line_boxes() as $line) {
+ if (!$line->inline) {
+ continue;
+ }
+
+ $line->trim_trailing_ws();
+
+ if ($line->left) {
+ foreach ($line->frames_to_align() as $frame) {
+ $frame->move($line->left, 0);
+ }
+ }
+ }
+ break;
+
+ case "right":
+ foreach ($this->_frame->get_line_boxes() as $i => $line) {
+ if (!$line->inline) {
+ continue;
+ }
+
+ $line->trim_trailing_ws();
+
+ $indent = $i === 0 ? $text_indent : 0;
+ $dx = $width - $line->w - $line->right - $indent;
+
+ foreach ($line->frames_to_align() as $frame) {
+ $frame->move($dx, 0);
+ }
+ }
+ break;
+
+ case "justify":
+ // We justify all lines except the last one, unless the frame
+ // has been split, in which case the actual last line is part of
+ // the split-off frame
+ $lines = $this->_frame->get_line_boxes();
+ $last_line_index = $this->_frame->is_split ? null : count($lines) - 1;
+
+ foreach ($lines as $i => $line) {
+ if (!$line->inline) {
+ continue;
+ }
+
+ $line->trim_trailing_ws();
+
+ if ($line->left) {
+ foreach ($line->frames_to_align() as $frame) {
+ $frame->move($line->left, 0);
+ }
+ }
+
+ if ($line->br || $i === $last_line_index) {
+ continue;
+ }
+
+ $frames = $line->get_frames();
+ $other_frame_count = 0;
+
+ foreach ($frames as $frame) {
+ if (!($frame instanceof TextFrameDecorator)) {
+ $other_frame_count++;
+ }
+ }
+
+ $word_count = $line->wc + $other_frame_count;
+
+ // Set the spacing for each child
+ if ($word_count > 1) {
+ $indent = $i === 0 ? $text_indent : 0;
+ $spacing = ($width - $line->get_width() - $indent) / ($word_count - 1);
+ } else {
+ $spacing = 0;
+ }
+
+ $dx = 0;
+ foreach ($frames as $frame) {
+ if ($frame instanceof TextFrameDecorator) {
+ $text = $frame->get_text();
+ $spaces = mb_substr_count($text, " ");
+
+ $frame->move($dx, 0);
+ $frame->set_text_spacing($spacing);
+
+ $dx += $spaces * $spacing;
+ } else {
+ $frame->move($dx, 0);
+ }
+ }
+
+ // The line (should) now occupy the entire width
+ $line->w = $width;
+ }
+ break;
+
+ case "center":
+ case "centre":
+ foreach ($this->_frame->get_line_boxes() as $i => $line) {
+ if (!$line->inline) {
+ continue;
+ }
+
+ $line->trim_trailing_ws();
+
+ $indent = $i === 0 ? $text_indent : 0;
+ $dx = ($width + $line->left - $line->w - $line->right - $indent) / 2;
+
+ foreach ($line->frames_to_align() as $frame) {
+ $frame->move($dx, 0);
+ }
+ }
+ break;
+ }
+ }
+
+ /**
+ * Align inline children vertically.
+ * Aligns each child vertically after each line is reflowed
+ */
+ function vertical_align()
+ {
+ $fontMetrics = $this->get_dompdf()->getFontMetrics();
+
+ foreach ($this->_frame->get_line_boxes() as $line) {
+ $height = $line->h;
+
+ // Move all markers to the top of the line box
+ foreach ($line->get_list_markers() as $marker) {
+ $x = $marker->get_position("x");
+ $marker->set_position($x, $line->y);
+ }
+
+ foreach ($line->frames_to_align() as $frame) {
+ $style = $frame->get_style();
+ $isInlineBlock = $style->display !== "inline"
+ && $style->display !== "-dompdf-list-bullet";
+
+ $baseline = $fontMetrics->getFontBaseline($style->font_family, $style->font_size);
+ $y_offset = 0;
+
+ //FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?)
+ if ($isInlineBlock) {
+ // Workaround: Skip vertical alignment if the frame is the
+ // only one one the line, excluding empty text frames, which
+ // may be the result of trailing white space
+ // FIXME: This special case should be removed once vertical
+ // alignment is properly fixed
+ $skip = true;
+
+ foreach ($line->get_frames() as $other) {
+ if ($other !== $frame
+ && !($other->is_text_node() && $other->get_node()->nodeValue === "")
+ ) {
+ $skip = false;
+ break;
+ }
+ }
+
+ if ($skip) {
+ continue;
+ }
+
+ $marginHeight = $frame->get_margin_height();
+ $imageHeightDiff = $height * 0.8 - $marginHeight;
+
+ $align = $frame->get_style()->vertical_align;
+ if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
+ switch ($align) {
+ case "middle":
+ $y_offset = $imageHeightDiff / 2;
+ break;
+
+ case "sub":
+ $y_offset = 0.3 * $height + $imageHeightDiff;
+ break;
+
+ case "super":
+ $y_offset = -0.2 * $height + $imageHeightDiff;
+ break;
+
+ case "text-top": // FIXME: this should be the height of the frame minus the height of the text
+ $y_offset = $height - $style->line_height;
+ break;
+
+ case "top":
+ break;
+
+ case "text-bottom": // FIXME: align bottom of image with the descender?
+ case "bottom":
+ $y_offset = 0.3 * $height + $imageHeightDiff;
+ break;
+
+ case "baseline":
+ default:
+ $y_offset = $imageHeightDiff;
+ break;
+ }
+ } else {
+ $y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - $marginHeight;
+ }
+ } else {
+ $parent = $frame->get_parent();
+ if ($parent instanceof TableCellFrameDecorator) {
+ $align = "baseline";
+ } else {
+ $align = $parent->get_style()->vertical_align;
+ }
+ if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
+ switch ($align) {
+ case "middle":
+ $y_offset = ($height * 0.8 - $baseline) / 2;
+ break;
+
+ case "sub":
+ $y_offset = $height * 0.8 - $baseline * 0.5;
+ break;
+
+ case "super":
+ $y_offset = $height * 0.8 - $baseline * 1.4;
+ break;
+
+ case "text-top":
+ case "top": // Not strictly accurate, but good enough for now
+ break;
+
+ case "text-bottom":
+ case "bottom":
+ $y_offset = $height * 0.8 - $baseline;
+ break;
+
+ case "baseline":
+ default:
+ $y_offset = $height * 0.8 - $baseline;
+ break;
+ }
+ } else {
+ $y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size);
+ }
+ }
+
+ if ($y_offset !== 0) {
+ $frame->move(0, $y_offset);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param AbstractFrameDecorator $child
+ */
+ function process_clear(AbstractFrameDecorator $child)
+ {
+ $child_style = $child->get_style();
+ $root = $this->_frame->get_root();
+
+ // Handle "clear"
+ if ($child_style->clear !== "none") {
+ //TODO: this is a WIP for handling clear/float frames that are in between inline frames
+ if ($child->get_prev_sibling() !== null) {
+ $this->_frame->add_line();
+ }
+ if ($child_style->float !== "none" && $child->get_next_sibling()) {
+ $this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1);
+ }
+
+ $lowest_y = $root->get_lowest_float_offset($child);
+
+ // If a float is still applying, we handle it
+ if ($lowest_y) {
+ if ($child->is_in_flow()) {
+ $line_box = $this->_frame->get_current_line_box();
+ $line_box->y = $lowest_y + $child->get_margin_height();
+ $line_box->left = 0;
+ $line_box->right = 0;
+ }
+
+ $child->move(0, $lowest_y - $child->get_position("y"));
+ }
+ }
+ }
+
+ /**
+ * @param AbstractFrameDecorator $child
+ * @param float $cb_x
+ * @param float $cb_w
+ */
+ function process_float(AbstractFrameDecorator $child, $cb_x, $cb_w)
+ {
+ $child_style = $child->get_style();
+ $root = $this->_frame->get_root();
+
+ // Handle "float"
+ if ($child_style->float !== "none") {
+ $root->add_floating_frame($child);
+
+ // Remove next frame's beginning whitespace
+ $next = $child->get_next_sibling();
+ if ($next && $next instanceof TextFrameDecorator) {
+ $next->set_text(ltrim($next->get_text()));
+ }
+
+ $line_box = $this->_frame->get_current_line_box();
+ list($old_x, $old_y) = $child->get_position();
+
+ $float_x = $cb_x;
+ $float_y = $old_y;
+ $float_w = $child->get_margin_width();
+
+ if ($child_style->clear === "none") {
+ switch ($child_style->float) {
+ case "left":
+ $float_x += $line_box->left;
+ break;
+ case "right":
+ $float_x += ($cb_w - $line_box->right - $float_w);
+ break;
+ }
+ } else {
+ if ($child_style->float === "right") {
+ $float_x += ($cb_w - $float_w);
+ }
+ }
+
+ if ($cb_w < $float_x + $float_w - $old_x) {
+ // TODO handle when floating elements don't fit
+ }
+
+ $line_box->get_float_offsets();
+
+ if ($child->_float_next_line) {
+ $float_y += $line_box->h;
+ }
+
+ $child->set_position($float_x, $float_y);
+ $child->move($float_x - $old_x, $float_y - $old_y, true);
+ }
+ }
+
+ /**
+ * @param BlockFrameDecorator $block
+ */
+ function reflow(BlockFrameDecorator $block = null)
+ {
+
+ // Check if a page break is forced
+ $page = $this->_frame->get_root();
+ $page->check_forced_page_break($this->_frame);
+
+ // Bail if the page is full
+ if ($page->is_full()) {
+ return;
+ }
+
+ $this->determine_absolute_containing_block();
+
+ // Counters and generated content
+ $this->_set_content();
+
+ // Inherit any dangling list markers
+ if ($block && $this->_frame->is_in_flow()) {
+ $this->_frame->inherit_dangling_markers($block);
+ }
+
+ // Collapse margins if required
+ $this->_collapse_margins();
+
+ $style = $this->_frame->get_style();
+ $cb = $this->_frame->get_containing_block();
+
+ // Determine the constraints imposed by this frame: calculate the width
+ // of the content area:
+ [$width, $margin_left, $margin_right, $left, $right] = $this->_calculate_restricted_width();
+
+ // Store the calculated properties
+ $style->set_used("width", $width);
+ $style->set_used("margin_left", $margin_left);
+ $style->set_used("margin_right", $margin_right);
+ $style->set_used("left", $left);
+ $style->set_used("right", $right);
+
+ $margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
+ $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
+
+ $auto_top = $style->top === "auto";
+ $auto_margin_top = $margin_top === "auto";
+
+ // Update the position
+ $this->_frame->position();
+ [$x, $y] = $this->_frame->get_position();
+
+ // Adjust the first line based on the text-indent property
+ $indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]);
+ $this->_frame->increase_line_width($indent);
+
+ // Determine the content edge
+ $top = (float)$style->length_in_pt([
+ $margin_top !== "auto" ? $margin_top : 0,
+ $style->border_top_width,
+ $style->padding_top
+ ], $cb["w"]);
+ $bottom = (float)$style->length_in_pt([
+ $margin_bottom !== "auto" ? $margin_bottom : 0,
+ $style->border_bottom_width,
+ $style->padding_bottom
+ ], $cb["w"]);
+
+ $cb_x = $x + (float)$margin_left + (float)$style->length_in_pt([$style->border_left_width,
+ $style->padding_left], $cb["w"]);
+
+ $cb_y = $y + $top;
+
+ $height = $style->length_in_pt($style->height, $cb["h"]);
+ if ($height === "auto") {
+ $height = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
+ }
+
+ // Set the y position of the first line in this block
+ $line_box = $this->_frame->get_current_line_box();
+ $line_box->y = $cb_y;
+ $line_box->get_float_offsets();
+
+ // Set the containing blocks and reflow each child
+ foreach ($this->_frame->get_children() as $child) {
+ $child->set_containing_block($cb_x, $cb_y, $width, $height);
+ $this->process_clear($child);
+ $child->reflow($this->_frame);
+
+ // Check for a page break before the child
+ $page->check_page_break($child);
+
+ // Don't add the child to the line if a page break has occurred
+ // before it (possibly via a descendant), in which case it has been
+ // reset, including its position
+ if ($page->is_full() && $child->get_position("x") === null) {
+ break;
+ }
+
+ $this->process_float($child, $cb_x, $width);
+ }
+
+ // Stop reflow if a page break has occurred before the frame, in which
+ // case it has been reset, including its position
+ if ($page->is_full() && $this->_frame->get_position("x") === null) {
+ return;
+ }
+
+ // Determine our height
+ [$height, $margin_top, $margin_bottom, $top, $bottom] = $this->_calculate_restricted_height();
+
+ $style->set_used("height", $height);
+ $style->set_used("margin_top", $margin_top);
+ $style->set_used("margin_bottom", $margin_bottom);
+ $style->set_used("top", $top);
+ $style->set_used("bottom", $bottom);
+
+ if ($this->_frame->is_absolute()) {
+ if ($auto_top) {
+ $this->_frame->move(0, $top);
+ }
+ if ($auto_margin_top) {
+ $this->_frame->move(0, $margin_top, true);
+ }
+ }
+
+ $this->_text_align();
+ $this->vertical_align();
+
+ // Handle relative positioning
+ foreach ($this->_frame->get_children() as $child) {
+ $this->position_relative($child);
+ }
+
+ if ($block && $this->_frame->is_in_flow()) {
+ $block->add_frame_to_line($this->_frame);
+
+ if ($this->_frame->is_block_level()) {
+ $block->add_line();
+ }
+ }
+ }
+
+ public function get_min_max_content_width(): array
+ {
+ // TODO: While the containing block is not set yet on the frame, it can
+ // already be determined in some cases due to fixed dimensions on the
+ // ancestor forming the containing block. In such cases, percentage
+ // values could be resolved here
+ $style = $this->_frame->get_style();
+ $width = $style->width;
+ $fixed_width = $width !== "auto" && !Helpers::is_percent($width);
+
+ // If the frame has a specified width, then we don't need to check
+ // its children
+ if ($fixed_width) {
+ $min = (float) $style->length_in_pt($width, 0);
+ $max = $min;
+ } else {
+ [$min, $max] = $this->get_min_max_child_width();
+ }
+
+ // Handle min/max width style properties
+ $min_width = $this->resolve_min_width(null);
+ $max_width = $this->resolve_max_width(null);
+ $min = Helpers::clamp($min, $min_width, $max_width);
+ $max = Helpers::clamp($max, $min_width, $max_width);
+
+ return [$min, $max];
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Image.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Image.php
new file mode 100644
index 000000000..eae34087b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Image.php
@@ -0,0 +1,213 @@
+determine_absolute_containing_block();
+
+ // Counters and generated content
+ $this->_set_content();
+
+ //FLOAT
+ //$frame = $this->_frame;
+ //$page = $frame->get_root();
+
+ //if ($frame->get_style()->float !== "none" ) {
+ // $page->add_floating_frame($this);
+ //}
+
+ $this->resolve_dimensions();
+ $this->resolve_margins();
+
+ $frame = $this->_frame;
+ $frame->position();
+
+ if ($block && $frame->is_in_flow()) {
+ $block->add_frame_to_line($frame);
+ }
+ }
+
+ public function get_min_max_content_width(): array
+ {
+ // TODO: While the containing block is not set yet on the frame, it can
+ // already be determined in some cases due to fixed dimensions on the
+ // ancestor forming the containing block. In such cases, percentage
+ // values could be resolved here
+ $style = $this->_frame->get_style();
+
+ [$width] = $this->calculate_size(null, null);
+ $min_width = $this->resolve_min_width(null);
+ $percent_width = Helpers::is_percent($style->width)
+ || Helpers::is_percent($style->max_width)
+ || ($style->width === "auto"
+ && (Helpers::is_percent($style->height) || Helpers::is_percent($style->max_height)));
+
+ // Use the specified min width as minimum when width or max width depend
+ // on the containing block and cannot be resolved yet. This mimics
+ // browser behavior
+ $min = $percent_width ? $min_width : $width;
+ $max = $width;
+
+ return [$min, $max];
+ }
+
+ /**
+ * Calculate width and height, accounting for min/max constraints.
+ *
+ * * https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width
+ * * https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height
+ * * https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
+ * * https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
+ *
+ * @param float|null $cbw Width of the containing block.
+ * @param float|null $cbh Height of the containing block.
+ *
+ * @return float[]
+ */
+ protected function calculate_size(?float $cbw, ?float $cbh): array
+ {
+ /** @var ImageFrameDecorator */
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+
+ $computed_width = $style->width;
+ $computed_height = $style->height;
+
+ $width = $cbw === null && Helpers::is_percent($computed_width)
+ ? "auto"
+ : $style->length_in_pt($computed_width, $cbw ?? 0);
+ $height = $cbh === null && Helpers::is_percent($computed_height)
+ ? "auto"
+ : $style->length_in_pt($computed_height, $cbh ?? 0);
+ $min_width = $this->resolve_min_width($cbw);
+ $max_width = $this->resolve_max_width($cbw);
+ $min_height = $this->resolve_min_height($cbh);
+ $max_height = $this->resolve_max_height($cbh);
+
+ if ($width === "auto" && $height === "auto") {
+ // Use intrinsic dimensions, resampled to pt
+ [$img_width, $img_height] = $frame->get_intrinsic_dimensions();
+ $w = $frame->resample($img_width);
+ $h = $frame->resample($img_height);
+
+ // Resolve min/max constraints according to the constraint-violation
+ // table in https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
+ $max_width = max($min_width, $max_width);
+ $max_height = max($min_height, $max_height);
+
+ if (($w > $max_width && $h <= $max_height)
+ || ($w > $max_width && $h > $max_height && $max_width / $w <= $max_height / $h)
+ || ($w < $min_width && $h > $min_height)
+ || ($w < $min_width && $h < $min_height && $min_width / $w > $min_height / $h)
+ ) {
+ $width = Helpers::clamp($w, $min_width, $max_width);
+ $height = $width * ($img_height / $img_width);
+ $height = Helpers::clamp($height, $min_height, $max_height);
+ } else {
+ $height = Helpers::clamp($h, $min_height, $max_height);
+ $width = $height * ($img_width / $img_height);
+ $width = Helpers::clamp($width, $min_width, $max_width);
+ }
+ } elseif ($height === "auto") {
+ // Width is fixed, scale height according to aspect ratio
+ [$img_width, $img_height] = $frame->get_intrinsic_dimensions();
+ $width = Helpers::clamp((float) $width, $min_width, $max_width);
+ $height = $width * ($img_height / $img_width);
+ $height = Helpers::clamp($height, $min_height, $max_height);
+ } elseif ($width === "auto") {
+ // Height is fixed, scale width according to aspect ratio
+ [$img_width, $img_height] = $frame->get_intrinsic_dimensions();
+ $height = Helpers::clamp((float) $height, $min_height, $max_height);
+ $width = $height * ($img_width / $img_height);
+ $width = Helpers::clamp($width, $min_width, $max_width);
+ } else {
+ // Width and height are fixed
+ $width = Helpers::clamp((float) $width, $min_width, $max_width);
+ $height = Helpers::clamp((float) $height, $min_height, $max_height);
+ }
+
+ return [$width, $height];
+ }
+
+ protected function resolve_dimensions(): void
+ {
+ /** @var ImageFrameDecorator */
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+
+ $debug_png = $this->get_dompdf()->getOptions()->getDebugPng();
+
+ if ($debug_png) {
+ [$img_width, $img_height] = $frame->get_intrinsic_dimensions();
+ print "resolve_dimensions() " .
+ $frame->get_style()->width . " " .
+ $frame->get_style()->height . ";" .
+ $frame->get_parent()->get_style()->width . " " .
+ $frame->get_parent()->get_style()->height . ";" .
+ $frame->get_parent()->get_parent()->get_style()->width . " " .
+ $frame->get_parent()->get_parent()->get_style()->height . ";" .
+ $img_width . " " .
+ $img_height . "|";
+ }
+
+ [, , $cbw, $cbh] = $frame->get_containing_block();
+ [$width, $height] = $this->calculate_size($cbw, $cbh);
+
+ if ($debug_png) {
+ print $width . " " . $height . ";";
+ }
+
+ $style->set_used("width", $width);
+ $style->set_used("height", $height);
+ }
+
+ protected function resolve_margins(): void
+ {
+ // Only handle the inline case for now
+ // https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width
+ // https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height
+ $style = $this->_frame->get_style();
+
+ if ($style->margin_left === "auto") {
+ $style->set_used("margin_left", 0.0);
+ }
+ if ($style->margin_right === "auto") {
+ $style->set_used("margin_right", 0.0);
+ }
+ if ($style->margin_top === "auto") {
+ $style->set_used("margin_top", 0.0);
+ }
+ if ($style->margin_bottom === "auto") {
+ $style->set_used("margin_bottom", 0.0);
+ }
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Inline.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Inline.php
new file mode 100644
index 000000000..34a66860d
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Inline.php
@@ -0,0 +1,188 @@
+_frame;
+ $style = $frame->get_style();
+
+ // Resolve width, so the margin width can be checked
+ $style->set_used("width", 0.0);
+
+ $cb = $frame->get_containing_block();
+ $line = $block->get_current_line_box();
+ $width = $frame->get_margin_width();
+
+ if ($width > ($cb["w"] - $line->left - $line->w - $line->right)) {
+ $block->add_line();
+
+ // Find the appropriate inline ancestor to split
+ $child = $frame;
+ $p = $child->get_parent();
+ while ($p instanceof InlineFrameDecorator && !$child->get_prev_sibling()) {
+ $child = $p;
+ $p = $p->get_parent();
+ }
+
+ if ($p instanceof InlineFrameDecorator) {
+ // Split parent and stop current reflow. Reflow continues
+ // via child-reflow loop of split parent
+ $p->split($child);
+ return;
+ }
+ }
+
+ $frame->position();
+ $block->add_frame_to_line($frame);
+ }
+
+ /**
+ * @param BlockFrameDecorator|null $block
+ */
+ function reflow(BlockFrameDecorator $block = null)
+ {
+ /** @var InlineFrameDecorator */
+ $frame = $this->_frame;
+
+ // Check if a page break is forced
+ $page = $frame->get_root();
+ $page->check_forced_page_break($frame);
+
+ if ($page->is_full()) {
+ return;
+ }
+
+ // Counters and generated content
+ $this->_set_content();
+
+ $style = $frame->get_style();
+
+ // Resolve auto margins
+ // https://www.w3.org/TR/CSS21/visudet.html#inline-width
+ // https://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced
+ if ($style->margin_left === "auto") {
+ $style->set_used("margin_left", 0.0);
+ }
+ if ($style->margin_right === "auto") {
+ $style->set_used("margin_right", 0.0);
+ }
+ if ($style->margin_top === "auto") {
+ $style->set_used("margin_top", 0.0);
+ }
+ if ($style->margin_bottom === "auto") {
+ $style->set_used("margin_bottom", 0.0);
+ }
+
+ // Handle line breaks
+ if ($frame->get_node()->nodeName === "br") {
+ if ($block) {
+ $line = $block->get_current_line_box();
+ $frame->set_containing_line($line);
+ $block->maximize_line_height($frame->get_margin_height(), $frame);
+ $block->add_line(true);
+
+ $next = $frame->get_next_sibling();
+ $p = $frame->get_parent();
+
+ if ($next && $p instanceof InlineFrameDecorator) {
+ $p->split($next);
+ }
+ }
+ return;
+ }
+
+ // Handle empty inline frames
+ if (!$frame->get_first_child()) {
+ if ($block) {
+ $this->reflow_empty($block);
+ }
+ return;
+ }
+
+ // Add our margin, padding & border to the first and last children
+ if (($f = $frame->get_first_child()) && $f instanceof TextFrameDecorator) {
+ $f_style = $f->get_style();
+ $f_style->margin_left = $style->margin_left;
+ $f_style->padding_left = $style->padding_left;
+ $f_style->border_left_width = $style->border_left_width;
+ $f_style->border_left_style = $style->border_left_style;
+ $f_style->border_left_color = $style->border_left_color;
+ }
+
+ if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) {
+ $l_style = $l->get_style();
+ $l_style->margin_right = $style->margin_right;
+ $l_style->padding_right = $style->padding_right;
+ $l_style->border_right_width = $style->border_right_width;
+ $l_style->border_right_style = $style->border_right_style;
+ $l_style->border_right_color = $style->border_right_color;
+ }
+
+ $cb = $frame->get_containing_block();
+
+ // Set the containing blocks and reflow each child. The containing
+ // block is not changed by line boxes.
+ foreach ($frame->get_children() as $child) {
+ $child->set_containing_block($cb);
+ $child->reflow($block);
+
+ // Stop reflow if the frame has been reset by a line or page break
+ // due to child reflow
+ if (!$frame->content_set) {
+ return;
+ }
+ }
+
+ if (!$frame->get_first_child()) {
+ return;
+ }
+
+ // Assume the position of the first child
+ [$x, $y] = $frame->get_first_child()->get_position();
+ $frame->set_position($x, $y);
+
+ // Handle relative positioning
+ foreach ($frame->get_children() as $child) {
+ $this->position_relative($child);
+ }
+
+ if ($block) {
+ $block->add_frame_to_line($frame);
+ }
+ }
+}
diff --git a/library/vendor/dompdf/src/FrameReflower/ListBullet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/ListBullet.php
similarity index 53%
rename from library/vendor/dompdf/src/FrameReflower/ListBullet.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/ListBullet.php
index 48613ccff..f735bae16 100644
--- a/library/vendor/dompdf/src/FrameReflower/ListBullet.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/ListBullet.php
@@ -1,14 +1,13 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameReflower;
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
-use Dompdf\FrameDecorator\AbstractFrameDecorator;
+use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator;
/**
* Reflows list bullets
@@ -20,9 +19,9 @@ class ListBullet extends AbstractFrameReflower
/**
* ListBullet constructor.
- * @param AbstractFrameDecorator $frame
+ * @param ListBulletFrameDecorator $frame
*/
- function __construct(AbstractFrameDecorator $frame)
+ function __construct(ListBulletFrameDecorator $frame)
{
parent::__construct($frame);
}
@@ -32,14 +31,17 @@ class ListBullet extends AbstractFrameReflower
*/
function reflow(BlockFrameDecorator $block = null)
{
- $style = $this->_frame->get_style();
+ /** @var ListBulletFrameDecorator */
+ $frame = $this->_frame;
+ $style = $frame->get_style();
- $style->width = $this->_frame->get_width();
- $this->_frame->position();
+ $style->set_used("width", $frame->get_width());
+ $frame->position();
if ($style->list_style_position === "inside") {
- $p = $this->_frame->find_block_parent();
- $p->add_frame_to_line($this->_frame);
+ $block->add_frame_to_line($frame);
+ } else {
+ $block->add_dangling_marker($frame);
}
}
}
diff --git a/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/NullFrameReflower.php
similarity index 87%
rename from library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/NullFrameReflower.php
index 8bdb0f1ad..8d7e5583c 100644
--- a/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/NullFrameReflower.php
@@ -1,11 +1,9 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf\FrameReflower;
use Dompdf\Frame;
diff --git a/library/vendor/dompdf/src/FrameReflower/Page.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Page.php
similarity index 76%
rename from library/vendor/dompdf/src/FrameReflower/Page.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Page.php
index dc87705c1..923865b53 100644
--- a/library/vendor/dompdf/src/FrameReflower/Page.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Page.php
@@ -1,9 +1,7 @@
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameReflower;
@@ -44,8 +42,8 @@ class Page extends AbstractFrameReflower
}
/**
- * @param Frame $frame
- * @param $page_number
+ * @param PageFrameDecorator $frame
+ * @param int $page_number
*/
function apply_page_style(Frame $frame, $page_number)
{
@@ -83,6 +81,8 @@ class Page extends AbstractFrameReflower
$frame->set_style($style);
}
+
+ $frame->calculate_bottom_page_edge();
}
/**
@@ -93,18 +93,20 @@ class Page extends AbstractFrameReflower
*/
function reflow(BlockFrameDecorator $block = null)
{
- $fixed_children = array();
+ /** @var PageFrameDecorator $frame */
+ $frame = $this->_frame;
+ $child = $frame->get_first_child();
+ $fixed_children = [];
$prev_child = null;
- $child = $this->_frame->get_first_child();
$current_page = 0;
while ($child) {
- $this->apply_page_style($this->_frame, $current_page + 1);
+ $this->apply_page_style($frame, $current_page + 1);
- $style = $this->_frame->get_style();
+ $style = $frame->get_style();
// Pages are only concerned with margins
- $cb = $this->_frame->get_containing_block();
+ $cb = $frame->get_containing_block();
$left = (float)$style->length_in_pt($style->margin_left, $cb["w"]);
$right = (float)$style->length_in_pt($style->margin_right, $cb["w"]);
$top = (float)$style->length_in_pt($style->margin_top, $cb["h"]);
@@ -117,8 +119,7 @@ class Page extends AbstractFrameReflower
// Only if it's the first page, we save the nodes with a fixed position
if ($current_page == 0) {
- $children = $child->get_children();
- foreach ($children as $onechild) {
+ foreach ($child->get_children() as $onechild) {
if ($onechild->get_style()->position === "fixed") {
$fixed_children[] = $onechild->deep_copy();
}
@@ -145,13 +146,13 @@ class Page extends AbstractFrameReflower
$this->_check_callbacks("begin_page_render", $child);
// Render the page
- $this->_frame->get_renderer()->render($child);
+ $frame->get_renderer()->render($child);
// Check for end render callback
$this->_check_callbacks("end_page_render", $child);
if ($next_child) {
- $this->_frame->next_page();
+ $frame->next_page();
}
// Wait to dispose of all frames on the previous page
@@ -174,31 +175,24 @@ class Page extends AbstractFrameReflower
* Check for callbacks that need to be performed when a given event
* gets triggered on a page
*
- * @param string $event the type of event
- * @param Frame $frame the frame that event is triggered on
+ * @param string $event The type of event
+ * @param Frame $frame The frame that event is triggered on
*/
- protected function _check_callbacks($event, $frame)
+ protected function _check_callbacks(string $event, Frame $frame): void
{
if (!isset($this->_callbacks)) {
- $dompdf = $this->_frame->get_dompdf();
- $this->_callbacks = $dompdf->get_callbacks();
- $this->_canvas = $dompdf->get_canvas();
+ $dompdf = $this->get_dompdf();
+ $this->_callbacks = $dompdf->getCallbacks();
+ $this->_canvas = $dompdf->getCanvas();
}
- if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
- $info = array(
- 0 => $this->_canvas, "canvas" => $this->_canvas,
- 1 => $frame, "frame" => $frame,
- );
+ if (isset($this->_callbacks[$event])) {
$fs = $this->_callbacks[$event];
+ $canvas = $this->_canvas;
+ $fontMetrics = $this->get_dompdf()->getFontMetrics();
+
foreach ($fs as $f) {
- if (is_callable($f)) {
- if (is_array($f)) {
- $f[0]->{$f[1]}($info);
- } else {
- $f($info);
- }
- }
+ $f($frame, $canvas, $fontMetrics);
}
}
}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Table.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Table.php
new file mode 100644
index 000000000..5173738e9
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Table.php
@@ -0,0 +1,523 @@
+_state = null;
+ parent::__construct($frame);
+ }
+
+ /**
+ * State is held here so it needs to be reset along with the decorator
+ */
+ public function reset(): void
+ {
+ parent::reset();
+ $this->_state = null;
+ }
+
+ protected function _assign_widths()
+ {
+ $style = $this->_frame->get_style();
+
+ // Find the min/max width of the table and sort the columns into
+ // absolute/percent/auto arrays
+ $delta = $this->_state["width_delta"];
+ $min_width = $this->_state["min_width"];
+ $max_width = $this->_state["max_width"];
+ $percent_used = $this->_state["percent_used"];
+ $absolute_used = $this->_state["absolute_used"];
+ $auto_min = $this->_state["auto_min"];
+
+ $absolute =& $this->_state["absolute"];
+ $percent =& $this->_state["percent"];
+ $auto =& $this->_state["auto"];
+
+ // Determine the actual width of the table (excluding borders and
+ // padding)
+ $cb = $this->_frame->get_containing_block();
+ $columns =& $this->_frame->get_cellmap()->get_columns();
+
+ $width = $style->width;
+ $min_table_width = $this->resolve_min_width($cb["w"]) - $delta;
+
+ if ($width !== "auto") {
+ $preferred_width = (float) $style->length_in_pt($width, $cb["w"]) - $delta;
+
+ if ($preferred_width < $min_table_width) {
+ $preferred_width = $min_table_width;
+ }
+
+ if ($preferred_width > $min_width) {
+ $width = $preferred_width;
+ } else {
+ $width = $min_width;
+ }
+
+ } else {
+ if ($max_width + $delta < $cb["w"]) {
+ $width = $max_width;
+ } elseif ($cb["w"] - $delta > $min_width) {
+ $width = $cb["w"] - $delta;
+ } else {
+ $width = $min_width;
+ }
+
+ if ($width < $min_table_width) {
+ $width = $min_table_width;
+ }
+
+ }
+
+ // Store our resolved width
+ $style->set_used("width", $width);
+
+ $cellmap = $this->_frame->get_cellmap();
+
+ if ($cellmap->is_columns_locked()) {
+ return;
+ }
+
+ // If the whole table fits on the page, then assign each column it's max width
+ if ($width == $max_width) {
+ foreach ($columns as $i => $col) {
+ $cellmap->set_column_width($i, $col["max-width"]);
+ }
+
+ return;
+ }
+
+ // Determine leftover and assign it evenly to all columns
+ if ($width > $min_width) {
+ // We have three cases to deal with:
+ //
+ // 1. All columns are auto or absolute width. In this case we
+ // distribute extra space across all auto columns weighted by the
+ // difference between their max and min width, or by max width only
+ // if the width of the table is larger than the max width for all
+ // columns.
+ //
+ // 2. Only absolute widths have been specified, no auto columns. In
+ // this case we distribute extra space across all columns weighted
+ // by their absolute width.
+ //
+ // 3. Percentage widths have been specified. In this case we normalize
+ // the percentage values and try to assign widths as fractions of
+ // the table width. Absolute column widths are fully satisfied and
+ // any remaining space is evenly distributed among all auto columns.
+
+ // Case 1:
+ if ($percent_used == 0 && count($auto)) {
+ foreach ($absolute as $i) {
+ $w = $columns[$i]["min-width"];
+ $cellmap->set_column_width($i, $w);
+ }
+
+ if ($width < $max_width) {
+ $increment = $width - $min_width;
+ $table_delta = $max_width - $min_width;
+
+ foreach ($auto as $i) {
+ $min = $columns[$i]["min-width"];
+ $max = $columns[$i]["max-width"];
+ $col_delta = $max - $min;
+ $w = $min + $increment * ($col_delta / $table_delta);
+ $cellmap->set_column_width($i, $w);
+ }
+ } else {
+ $increment = $width - $max_width;
+ $auto_max = $max_width - $absolute_used;
+
+ foreach ($auto as $i) {
+ $max = $columns[$i]["max-width"];
+ $f = $auto_max > 0 ? $max / $auto_max : 1 / count($auto);
+ $w = $max + $increment * $f;
+ $cellmap->set_column_width($i, $w);
+ }
+ }
+ return;
+ }
+
+ // Case 2:
+ if ($percent_used == 0 && !count($auto)) {
+ $increment = $width - $absolute_used;
+
+ foreach ($absolute as $i) {
+ $abs = $columns[$i]["min-width"];
+ $f = $absolute_used > 0 ? $abs / $absolute_used : 1 / count($absolute);
+ $w = $abs + $increment * $f;
+ $cellmap->set_column_width($i, $w);
+ }
+ return;
+ }
+
+ // Case 3:
+ if ($percent_used > 0) {
+ // Scale percent values if the total percentage is > 100 or
+ // there are no auto values to take up slack
+ if ($percent_used > 100 || count($auto) == 0) {
+ $scale = 100 / $percent_used;
+ } else {
+ $scale = 1;
+ }
+
+ // Account for the minimum space used by the unassigned auto
+ // columns, by the columns with absolute widths, and the
+ // percentage columns following the current one
+ $used_width = $auto_min + $absolute_used;
+
+ foreach ($absolute as $i) {
+ $w = $columns[$i]["min-width"];
+ $cellmap->set_column_width($i, $w);
+ }
+
+ $percent_min = 0;
+
+ foreach ($percent as $i) {
+ $percent_min += $columns[$i]["min-width"];
+ }
+
+ // First-come, first served
+ foreach ($percent as $i) {
+ $min = $columns[$i]["min-width"];
+ $percent_min -= $min;
+ $slack = $width - $used_width - $percent_min;
+
+ $columns[$i]["percent"] *= $scale;
+ $w = min($columns[$i]["percent"] * $width / 100, $slack);
+
+ if ($w < $min) {
+ $w = $min;
+ }
+
+ $cellmap->set_column_width($i, $w);
+ $used_width += $w;
+ }
+
+ // This works because $used_width includes the min-width of each
+ // unassigned column
+ if (count($auto) > 0) {
+ $increment = ($width - $used_width) / count($auto);
+
+ foreach ($auto as $i) {
+ $w = $columns[$i]["min-width"] + $increment;
+ $cellmap->set_column_width($i, $w);
+ }
+ }
+ return;
+ }
+ } else {
+ // We are over-constrained:
+ // Each column gets its minimum width
+ foreach ($columns as $i => $col) {
+ $cellmap->set_column_width($i, $col["min-width"]);
+ }
+ }
+ }
+
+ /**
+ * Determine the frame's height based on min/max height
+ *
+ * @return float
+ */
+ protected function _calculate_height()
+ {
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+ $cb = $frame->get_containing_block();
+
+ $height = $style->length_in_pt($style->height, $cb["h"]);
+
+ $cellmap = $frame->get_cellmap();
+ $cellmap->assign_frame_heights();
+ $rows = $cellmap->get_rows();
+
+ // Determine our content height
+ $content_height = 0.0;
+ foreach ($rows as $r) {
+ $content_height += $r["height"];
+ }
+
+ if ($height === "auto") {
+ $height = $content_height;
+ }
+
+ // Handle min/max height
+ // https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
+ $min_height = $this->resolve_min_height($cb["h"]);
+ $max_height = $this->resolve_max_height($cb["h"]);
+ $height = Helpers::clamp($height, $min_height, $max_height);
+
+ // Use the content height or the height value, whichever is greater
+ if ($height <= $content_height) {
+ $height = $content_height;
+ } else {
+ // FIXME: Borders and row positions are not properly updated by this
+ // $cellmap->set_frame_heights($height, $content_height);
+ }
+
+ return $height;
+ }
+
+ /**
+ * @param BlockFrameDecorator|null $block
+ */
+ function reflow(BlockFrameDecorator $block = null)
+ {
+ /** @var TableFrameDecorator */
+ $frame = $this->_frame;
+
+ // Check if a page break is forced
+ $page = $frame->get_root();
+ $page->check_forced_page_break($frame);
+
+ // Bail if the page is full
+ if ($page->is_full()) {
+ return;
+ }
+
+ // Let the page know that we're reflowing a table so that splits
+ // are suppressed (simply setting page-break-inside: avoid won't
+ // work because we may have an arbitrary number of block elements
+ // inside tds.)
+ $page->table_reflow_start();
+
+ $this->determine_absolute_containing_block();
+
+ // Counters and generated content
+ $this->_set_content();
+
+ // Collapse vertical margins, if required
+ $this->_collapse_margins();
+
+ // Table layout algorithm:
+ // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
+
+ if (is_null($this->_state)) {
+ $this->get_min_max_width();
+ }
+
+ $cb = $frame->get_containing_block();
+ $style = $frame->get_style();
+
+ // This is slightly inexact, but should be okay. Add half the
+ // border-spacing to the table as padding. The other half is added to
+ // the cells themselves.
+ if ($style->border_collapse === "separate") {
+ [$h, $v] = $style->border_spacing;
+ $v = $v / 2;
+ $h = $h / 2;
+
+ $style->set_used("padding_left", (float)$style->length_in_pt($style->padding_left, $cb["w"]) + $h);
+ $style->set_used("padding_right", (float)$style->length_in_pt($style->padding_right, $cb["w"]) + $h);
+ $style->set_used("padding_top", (float)$style->length_in_pt($style->padding_top, $cb["w"]) + $v);
+ $style->set_used("padding_bottom", (float)$style->length_in_pt($style->padding_bottom, $cb["w"]) + $v);
+ }
+
+ $this->_assign_widths();
+
+ // Adjust left & right margins, if they are auto
+ $delta = $this->_state["width_delta"];
+ $width = $style->width;
+ $left = $style->length_in_pt($style->margin_left, $cb["w"]);
+ $right = $style->length_in_pt($style->margin_right, $cb["w"]);
+
+ $diff = (float) $cb["w"] - (float) $width - $delta;
+
+ if ($left === "auto" && $right === "auto") {
+ if ($diff < 0) {
+ $left = 0;
+ $right = $diff;
+ } else {
+ $left = $right = $diff / 2;
+ }
+ } else {
+ if ($left === "auto") {
+ $left = max($diff - $right, 0);
+ }
+ if ($right === "auto") {
+ $right = max($diff - $left, 0);
+ }
+ }
+
+ $style->set_used("margin_left", $left);
+ $style->set_used("margin_right", $right);
+
+ $frame->position();
+ [$x, $y] = $frame->get_position();
+
+ // Determine the content edge
+ $offset_x = (float)$left + (float)$style->length_in_pt([
+ $style->padding_left,
+ $style->border_left_width
+ ], $cb["w"]);
+ $offset_y = (float)$style->length_in_pt([
+ $style->margin_top,
+ $style->border_top_width,
+ $style->padding_top
+ ], $cb["w"]);
+ $content_x = $x + $offset_x;
+ $content_y = $y + $offset_y;
+
+ if (isset($cb["h"])) {
+ $h = $cb["h"];
+ } else {
+ $h = null;
+ }
+
+ $cellmap = $frame->get_cellmap();
+ $col =& $cellmap->get_column(0);
+ $col["x"] = $offset_x;
+
+ $row =& $cellmap->get_row(0);
+ $row["y"] = $offset_y;
+
+ $cellmap->assign_x_positions();
+
+ // Set the containing block of each child & reflow
+ foreach ($frame->get_children() as $child) {
+ $child->set_containing_block($content_x, $content_y, $width, $h);
+ $child->reflow();
+
+ if (!$page->in_nested_table()) {
+ // Check if a split has occurred
+ $page->check_page_break($child);
+
+ if ($page->is_full()) {
+ break;
+ }
+ }
+ }
+
+ // Stop reflow if a page break has occurred before the frame, in which
+ // case it has been reset, including its position
+ if ($page->is_full() && $frame->get_position("x") === null) {
+ $page->table_reflow_end();
+ return;
+ }
+
+ // Assign heights to our cells:
+ $style->set_used("height", $this->_calculate_height());
+
+ $page->table_reflow_end();
+
+ if ($block && $frame->is_in_flow()) {
+ $block->add_frame_to_line($frame);
+
+ if ($frame->is_block_level()) {
+ $block->add_line();
+ }
+ }
+ }
+
+ public function get_min_max_width(): array
+ {
+ if (!is_null($this->_min_max_cache)) {
+ return $this->_min_max_cache;
+ }
+
+ $style = $this->_frame->get_style();
+ $cellmap = $this->_frame->get_cellmap();
+
+ $this->_frame->normalize();
+
+ // Add the cells to the cellmap (this will calculate column widths as
+ // frames are added)
+ $cellmap->add_frame($this->_frame);
+
+ // Find the min/max width of the table and sort the columns into
+ // absolute/percent/auto arrays
+ $this->_state = [];
+ $this->_state["min_width"] = 0;
+ $this->_state["max_width"] = 0;
+
+ $this->_state["percent_used"] = 0;
+ $this->_state["absolute_used"] = 0;
+ $this->_state["auto_min"] = 0;
+
+ $this->_state["absolute"] = [];
+ $this->_state["percent"] = [];
+ $this->_state["auto"] = [];
+
+ $columns =& $cellmap->get_columns();
+ foreach ($columns as $i => $col) {
+ $this->_state["min_width"] += $col["min-width"];
+ $this->_state["max_width"] += $col["max-width"];
+
+ if ($col["absolute"] > 0) {
+ $this->_state["absolute"][] = $i;
+ $this->_state["absolute_used"] += $col["min-width"];
+ } elseif ($col["percent"] > 0) {
+ $this->_state["percent"][] = $i;
+ $this->_state["percent_used"] += $col["percent"];
+ } else {
+ $this->_state["auto"][] = $i;
+ $this->_state["auto_min"] += $col["min-width"];
+ }
+ }
+
+ // Account for margins, borders, padding, and border spacing
+ $cb_w = $this->_frame->get_containing_block("w");
+ $lm = (float) $style->length_in_pt($style->margin_left, $cb_w);
+ $rm = (float) $style->length_in_pt($style->margin_right, $cb_w);
+
+ $dims = [
+ $style->border_left_width,
+ $style->border_right_width,
+ $style->padding_left,
+ $style->padding_right
+ ];
+
+ if ($style->border_collapse !== "collapse") {
+ list($dims[]) = $style->border_spacing;
+ }
+
+ $delta = (float) $style->length_in_pt($dims, $cb_w);
+
+ $this->_state["width_delta"] = $delta;
+
+ $min_width = $this->_state["min_width"] + $delta + $lm + $rm;
+ $max_width = $this->_state["max_width"] + $delta + $lm + $rm;
+
+ return $this->_min_max_cache = [
+ $min_width,
+ $max_width,
+ "min" => $min_width,
+ "max" => $max_width
+ ];
+ }
+}
diff --git a/library/vendor/dompdf/src/FrameReflower/TableCell.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableCell.php
similarity index 55%
rename from library/vendor/dompdf/src/FrameReflower/TableCell.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableCell.php
index 87c5cf818..f5ce35da6 100644
--- a/library/vendor/dompdf/src/FrameReflower/TableCell.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableCell.php
@@ -1,14 +1,14 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameReflower;
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
+use Dompdf\Helpers;
/**
* Reflows table cells
@@ -31,6 +31,9 @@ class TableCell extends Block
*/
function reflow(BlockFrameDecorator $block = null)
{
+ // Counters and generated content
+ $this->_set_content();
+
$style = $this->_frame->get_style();
$table = TableFrameDecorator::find_parent_table($this->_frame);
@@ -50,26 +53,27 @@ class TableCell extends Block
//FIXME?
$h = $this->_frame->get_containing_block("h");
- $left_space = (float)$style->length_in_pt(array($style->margin_left,
+ $left_space = (float)$style->length_in_pt([$style->margin_left,
$style->padding_left,
- $style->border_left_width),
+ $style->border_left_width],
$w);
- $right_space = (float)$style->length_in_pt(array($style->padding_right,
+ $right_space = (float)$style->length_in_pt([$style->padding_right,
$style->margin_right,
- $style->border_right_width),
+ $style->border_right_width],
$w);
- $top_space = (float)$style->length_in_pt(array($style->margin_top,
+ $top_space = (float)$style->length_in_pt([$style->margin_top,
$style->padding_top,
- $style->border_top_width),
+ $style->border_top_width],
$h);
- $bottom_space = (float)$style->length_in_pt(array($style->margin_bottom,
+ $bottom_space = (float)$style->length_in_pt([$style->margin_bottom,
$style->padding_bottom,
- $style->border_bottom_width),
+ $style->border_bottom_width],
$h);
- $style->width = $cb_w = $w - $left_space - $right_space;
+ $cb_w = $w - $left_space - $right_space;
+ $style->set_used("width", $cb_w);
$content_x = $x + $left_space;
$content_y = $line_y = $y + $top_space;
@@ -86,22 +90,25 @@ class TableCell extends Block
// Set the containing blocks and reflow each child
foreach ($this->_frame->get_children() as $child) {
- if ($page->is_full()) {
- break;
- }
-
$child->set_containing_block($content_x, $content_y, $cb_w, $h);
$this->process_clear($child);
$child->reflow($this->_frame);
- $this->process_float($child, $x + $left_space, $w - $right_space - $left_space);
+ $this->process_float($child, $content_x, $cb_w);
+
+ if ($page->is_full()) {
+ break;
+ }
}
// Determine our height
$style_height = (float)$style->length_in_pt($style->height, $h);
- $this->_frame->set_content_height($this->_calculate_content_height());
+ /** @var FrameDecorator\TableCell */
+ $frame = $this->_frame;
- $height = max($style_height, (float)$this->_frame->get_content_height());
+ $frame->set_content_height($this->_calculate_content_height());
+
+ $height = max($style_height, (float)$frame->get_content_height());
// Let the cellmap know our height
$cell_height = $height / count($cells["rows"]);
@@ -114,8 +121,41 @@ class TableCell extends Block
$cellmap->set_row_height($i, $cell_height);
}
- $style->height = $height;
+ $style->set_used("height", $height);
+
$this->_text_align();
$this->vertical_align();
+
+ // Handle relative positioning
+ foreach ($this->_frame->get_children() as $child) {
+ $this->position_relative($child);
+ }
+ }
+
+ public function get_min_max_content_width(): array
+ {
+ // Ignore percentage values for a specified width here, as they are
+ // relative to the table width, which is not determined yet
+ $style = $this->_frame->get_style();
+ $width = $style->width;
+ $fixed_width = $width !== "auto" && !Helpers::is_percent($width);
+
+ [$min, $max] = $this->get_min_max_child_width();
+
+ // For table cells: Use specified width if it is greater than the
+ // minimum defined by the content
+ if ($fixed_width) {
+ $width = (float) $style->length_in_pt($width, 0);
+ $min = max($width, $min);
+ $max = $min;
+ }
+
+ // Handle min/max width style properties
+ $min_width = $this->resolve_min_width(null);
+ $max_width = $this->resolve_max_width(null);
+ $min = Helpers::clamp($min, $min_width, $max_width);
+ $max = Helpers::clamp($max, $min_width, $max_width);
+
+ return [$min, $max];
}
}
diff --git a/library/vendor/dompdf/src/FrameReflower/TableRow.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRow.php
similarity index 72%
rename from library/vendor/dompdf/src/FrameReflower/TableRow.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRow.php
index 5b94473b9..f84c1258b 100644
--- a/library/vendor/dompdf/src/FrameReflower/TableRow.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRow.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameReflower;
@@ -33,23 +32,32 @@ class TableRow extends AbstractFrameReflower
*/
function reflow(BlockFrameDecorator $block = null)
{
- $page = $this->_frame->get_root();
+ /** @var TableRowFrameDecorator */
+ $frame = $this->_frame;
+ // Check if a page break is forced
+ $page = $frame->get_root();
+ $page->check_forced_page_break($frame);
+
+ // Bail if the page is full
if ($page->is_full()) {
return;
}
+ // Counters and generated content
+ $this->_set_content();
+
$this->_frame->position();
$style = $this->_frame->get_style();
$cb = $this->_frame->get_containing_block();
foreach ($this->_frame->get_children() as $child) {
- if ($page->is_full()) {
- return;
- }
-
$child->set_containing_block($cb);
$child->reflow();
+
+ if ($page->is_full()) {
+ break;
+ }
}
if ($page->is_full()) {
@@ -58,8 +66,8 @@ class TableRow extends AbstractFrameReflower
$table = TableFrameDecorator::find_parent_table($this->_frame);
$cellmap = $table->get_cellmap();
- $style->width = $cellmap->get_frame_width($this->_frame);
- $style->height = $cellmap->get_frame_height($this->_frame);
+ $style->set_used("width", $cellmap->get_frame_width($this->_frame));
+ $style->set_used("height", $cellmap->get_frame_height($this->_frame));
$this->_frame->set_position($cellmap->get_frame_position($this->_frame));
}
@@ -67,7 +75,7 @@ class TableRow extends AbstractFrameReflower
/**
* @throws Exception
*/
- function get_min_max_width()
+ public function get_min_max_width(): array
{
throw new Exception("Min/max width is undefined for table rows");
}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php
new file mode 100644
index 000000000..c8b19aa53
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php
@@ -0,0 +1,71 @@
+_frame;
+ $page = $frame->get_root();
+
+ // Counters and generated content
+ $this->_set_content();
+
+ $style = $frame->get_style();
+ $cb = $frame->get_containing_block();
+
+ foreach ($frame->get_children() as $child) {
+ $child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]);
+ $child->reflow();
+
+ // Check if a split has occurred
+ $page->check_page_break($child);
+
+ if ($page->is_full()) {
+ break;
+ }
+ }
+
+ $table = TableFrameDecorator::find_parent_table($frame);
+ $cellmap = $table->get_cellmap();
+
+ // Stop reflow if a page break has occurred before the frame, in which
+ // case it is not part of its parent table's cell map yet
+ if ($page->is_full() && !$cellmap->frame_exists_in_cellmap($frame)) {
+ return;
+ }
+
+ $style->set_used("width", $cellmap->get_frame_width($frame));
+ $style->set_used("height", $cellmap->get_frame_height($frame));
+
+ $frame->set_position($cellmap->get_frame_position($frame));
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Text.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Text.php
new file mode 100644
index 000000000..57439ae70
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Text.php
@@ -0,0 +1,605 @@
+
+ */
+ const SOFT_HYPHEN = "\xC2\xAD";
+
+ /**
+ * The regex splits on everything that's a separator (^\S double negative),
+ * excluding the following non-breaking space characters:
+ * * nbsp (\xA0)
+ * * narrow nbsp (\x{202F})
+ * * figure space (\x{2007})
+ */
+ public static $_whitespace_pattern = '/([^\S\xA0\x{202F}\x{2007}]+)/u';
+
+ /**
+ * The regex splits on everything that's a separator (^\S double negative)
+ * plus dashes, excluding the following non-breaking space characters:
+ * * nbsp (\xA0)
+ * * narrow nbsp (\x{202F})
+ * * figure space (\x{2007})
+ */
+ public static $_wordbreak_pattern = '/([^\S\xA0\x{202F}\x{2007}\n]+|\R|\-+|\xAD+)/u';
+
+ /**
+ * Frame for this reflower
+ *
+ * @var TextFrameDecorator
+ */
+ protected $_frame;
+
+ /**
+ * Saves trailing whitespace trimmed after a line break, so it can be
+ * restored when needed.
+ *
+ * @var string|null
+ */
+ protected $trailingWs = null;
+
+ /**
+ * @var FontMetrics
+ */
+ private $fontMetrics;
+
+ /**
+ * @param TextFrameDecorator $frame
+ * @param FontMetrics $fontMetrics
+ */
+ public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics)
+ {
+ parent::__construct($frame);
+ $this->setFontMetrics($fontMetrics);
+ }
+
+ /**
+ * Apply text transform and white-space collapse according to style.
+ *
+ * * http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
+ * * http://www.w3.org/TR/CSS21/text.html#propdef-white-space
+ *
+ * @param string $text
+ * @return string
+ */
+ protected function pre_process_text(string $text): string
+ {
+ $style = $this->_frame->get_style();
+
+ // Handle text transform
+ switch ($style->text_transform) {
+ case "capitalize":
+ $text = Helpers::mb_ucwords($text);
+ break;
+ case "uppercase":
+ $text = mb_convert_case($text, MB_CASE_UPPER);
+ break;
+ case "lowercase":
+ $text = mb_convert_case($text, MB_CASE_LOWER);
+ break;
+ default:
+ break;
+ }
+
+ // Handle white-space collapse
+ switch ($style->white_space) {
+ default:
+ case "normal":
+ case "nowrap":
+ $text = preg_replace(self::$_whitespace_pattern, " ", $text) ?? "";
+ break;
+
+ case "pre-line":
+ // Collapse white space except for line breaks
+ $text = preg_replace('/([^\S\xA0\x{202F}\x{2007}\n]+)/u', " ", $text) ?? "";
+ break;
+
+ case "pre":
+ case "pre-wrap":
+ break;
+
+ }
+
+ return $text;
+ }
+
+ /**
+ * @param string $text
+ * @param BlockFrameDecorator $block
+ * @param bool $nowrap
+ *
+ * @return bool|int
+ */
+ protected function line_break(string $text, BlockFrameDecorator $block, bool $nowrap = false)
+ {
+ $fontMetrics = $this->getFontMetrics();
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+ $font = $style->font_family;
+ $size = $style->font_size;
+ $word_spacing = $style->word_spacing;
+ $letter_spacing = $style->letter_spacing;
+
+ // Determine the available width
+ $current_line = $block->get_current_line_box();
+ $line_width = $frame->get_containing_block("w");
+ $current_line_width = $current_line->left + $current_line->w + $current_line->right;
+ $available_width = $line_width - $current_line_width;
+
+ // Determine the frame width including margin, padding & border
+ $visible_text = preg_replace('/\xAD/u', "", $text);
+ $text_width = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
+ $mbp_width = (float) $style->length_in_pt([
+ $style->margin_left,
+ $style->border_left_width,
+ $style->padding_left,
+ $style->padding_right,
+ $style->border_right_width,
+ $style->margin_right
+ ], $line_width);
+ $frame_width = $text_width + $mbp_width;
+
+ if (Helpers::lengthLessOrEqual($frame_width, $available_width)) {
+ return false;
+ }
+
+ if ($nowrap) {
+ return $current_line_width == 0 ? false : 0;
+ }
+
+ // Split the text into words
+ $words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $wc = count($words);
+
+ // Determine the split point
+ $width = 0.0;
+ $str = "";
+
+ $space_width = $fontMetrics->getTextWidth(" ", $font, $size, $word_spacing, $letter_spacing);
+ $shy_width = $fontMetrics->getTextWidth(self::SOFT_HYPHEN, $font, $size);
+
+ // @todo support
+ for ($i = 0; $i < $wc; $i += 2) {
+ // Allow trailing white space to overflow. White space is always
+ // collapsed to the standard space character currently, so only
+ // handle that for now
+ $sep = $words[$i + 1] ?? "";
+ $word = $sep === " " ? $words[$i] : $words[$i] . $sep;
+ $word_width = $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
+ $used_width = $width + $word_width + $mbp_width;
+
+ if (Helpers::lengthGreater($used_width, $available_width)) {
+ // If the previous split happened by soft hyphen, we have to
+ // append its width again because the last hyphen of a line
+ // won't be removed
+ if (isset($words[$i - 1]) && self::SOFT_HYPHEN === $words[$i - 1]) {
+ $width += $shy_width;
+ }
+ break;
+ }
+
+ // If the word is splitted by soft hyphen, but no line break is needed
+ // we have to reduce the width. But the str is not modified, otherwise
+ // the wrong offset is calculated at the end of this method.
+ if ($sep === self::SOFT_HYPHEN) {
+ $width += $word_width - $shy_width;
+ $str .= $word;
+ } elseif ($sep === " ") {
+ $width += $word_width + $space_width;
+ $str .= $word . $sep;
+ } else {
+ $width += $word_width;
+ $str .= $word;
+ }
+ }
+
+ // The first word has overflowed. Force it onto the line, or as many
+ // characters as fit if breaking words is allowed
+ if ($current_line_width == 0 && $width === 0.0) {
+ if ($sep === " ") {
+ $word .= $sep;
+ }
+
+ // https://www.w3.org/TR/css-text-3/#overflow-wrap-property
+ $wrap = $style->overflow_wrap;
+ $break_word = $wrap === "anywhere" || $wrap === "break-word";
+
+ if ($break_word) {
+ $s = "";
+
+ for ($j = 0; $j < mb_strlen($word); $j++) {
+ $c = mb_substr($word, $j, 1);
+ $w = $fontMetrics->getTextWidth($s . $c, $font, $size, $word_spacing, $letter_spacing);
+
+ if (Helpers::lengthGreater($w, $available_width)) {
+ break;
+ }
+
+ $s .= $c;
+ }
+
+ // Always force the first character onto the line
+ $str = $j === 0 ? $s . $c : $s;
+ } else {
+ $str = $word;
+ }
+ }
+
+ $offset = mb_strlen($str);
+ return $offset;
+ }
+
+ /**
+ * @param string $text
+ * @return bool|int
+ */
+ protected function newline_break(string $text)
+ {
+ if (($i = mb_strpos($text, "\n")) === false) {
+ return false;
+ }
+
+ return $i + 1;
+ }
+
+ /**
+ * @param BlockFrameDecorator $block
+ * @return bool|null Whether to add a new line at the end. `null` if reflow
+ * should be stopped.
+ */
+ protected function layout_line(BlockFrameDecorator $block): ?bool
+ {
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+ $current_line = $block->get_current_line_box();
+ $text = $frame->get_text();
+
+ // Trim leading white space if this is the first text on the line
+ if ($current_line->w === 0.0 && !$frame->is_pre()) {
+ $text = ltrim($text, " ");
+ }
+
+ // Exclude wrapped white space. This handles white space between block
+ // elements in case white space is collapsed
+ if ($text === "") {
+ $frame->set_text("");
+ $style->set_used("width", 0.0);
+ return null;
+ }
+
+ // Determine the next line break
+ // http://www.w3.org/TR/CSS21/text.html#propdef-white-space
+ $white_space = $style->white_space;
+ $nowrap = $white_space === "nowrap" || $white_space === "pre";
+
+ switch ($white_space) {
+ default:
+ case "normal":
+ case "nowrap":
+ $split = $this->line_break($text, $block, $nowrap);
+ $add_line = false;
+ break;
+
+ case "pre":
+ case "pre-line":
+ case "pre-wrap":
+ $hard_split = $this->newline_break($text);
+ $first_line = $hard_split !== false
+ ? mb_substr($text, 0, $hard_split)
+ : $text;
+ $soft_split = $this->line_break($first_line, $block, $nowrap);
+
+ $split = $soft_split !== false ? $soft_split : $hard_split;
+ $add_line = $hard_split !== false;
+ break;
+ }
+
+ if ($split === 0) {
+ // Make sure to move text when floating frames leave no space to
+ // place anything onto the line
+ // TODO: Would probably be better to move just below the current
+ // floating frame instead of trying to place text in line-height
+ // increments
+ if ($current_line->h === 0.0) {
+ // Line height might be 0
+ $h = max($frame->get_margin_height(), 1.0);
+ $block->maximize_line_height($h, $frame);
+ }
+
+ // Break line and repeat layout
+ $block->add_line();
+
+ // Find the appropriate inline ancestor to split
+ $child = $frame;
+ $p = $child->get_parent();
+ while ($p instanceof InlineFrameDecorator && !$child->get_prev_sibling()) {
+ $child = $p;
+ $p = $p->get_parent();
+ }
+
+ if ($p instanceof InlineFrameDecorator) {
+ // Split parent and stop current reflow. Reflow continues
+ // via child-reflow loop of split parent
+ $p->split($child);
+ return null;
+ }
+
+ return $this->layout_line($block);
+ }
+
+ // Final split point is determined
+ if ($split !== false && $split < mb_strlen($text)) {
+ // Split the line
+ $frame->set_text($text);
+ $frame->split_text($split);
+ $add_line = true;
+
+ // Remove inner soft hyphens
+ $t = $frame->get_text();
+ $shyPosition = mb_strpos($t, self::SOFT_HYPHEN);
+ if (false !== $shyPosition && $shyPosition < mb_strlen($t) - 1) {
+ $t = str_replace(self::SOFT_HYPHEN, "", mb_substr($t, 0, -1)) . mb_substr($t, -1);
+ $frame->set_text($t);
+ }
+ } else {
+ // No split required
+ // Remove soft hyphens
+ $text = str_replace(self::SOFT_HYPHEN, "", $text);
+ $frame->set_text($text);
+ }
+
+ // Set our new width
+ $frame->recalculate_width();
+
+ return $add_line;
+ }
+
+ /**
+ * @param BlockFrameDecorator|null $block
+ */
+ function reflow(BlockFrameDecorator $block = null)
+ {
+ $frame = $this->_frame;
+ $page = $frame->get_root();
+ $page->check_forced_page_break($frame);
+
+ if ($page->is_full()) {
+ return;
+ }
+
+ // Determine the text height
+ $style = $frame->get_style();
+ $size = $style->font_size;
+ $font = $style->font_family;
+ $font_height = $this->getFontMetrics()->getFontHeight($font, $size);
+ $style->set_used("height", $font_height);
+
+ // Handle text transform and white space
+ $text = $this->pre_process_text($frame->get_text());
+ $frame->set_text($text);
+
+ $add_line = $this->layout_line($block);
+
+ if ($add_line === null) {
+ return;
+ }
+
+ $frame->position();
+
+ if ($block) {
+ $line = $block->add_frame_to_line($frame);
+ $trimmed = trim($frame->get_text());
+
+ // Split the text into words (used to determine spacing between
+ // words on justified lines)
+ if ($trimmed !== "") {
+ $words = preg_split(self::$_whitespace_pattern, $trimmed);
+ $line->wc += count($words);
+ }
+
+ if ($add_line) {
+ $block->add_line();
+ }
+ }
+ }
+
+ /**
+ * Trim trailing white space from the frame text.
+ */
+ public function trim_trailing_ws(): void
+ {
+ $frame = $this->_frame;
+ $text = $frame->get_text();
+ $trailing = mb_substr($text, -1);
+
+ // White space is always collapsed to the standard space character
+ // currently, so only handle that for now
+ if ($trailing === " ") {
+ $this->trailingWs = $trailing;
+ $frame->set_text(mb_substr($text, 0, -1));
+ $frame->recalculate_width();
+ }
+ }
+
+ public function reset(): void
+ {
+ parent::reset();
+
+ // Restore trimmed trailing white space, as the frame will go through
+ // another reflow and line breaks might be different after a split
+ if ($this->trailingWs !== null) {
+ $text = $this->_frame->get_text();
+ $this->_frame->set_text($text . $this->trailingWs);
+ $this->trailingWs = null;
+ }
+ }
+
+ //........................................................................
+
+ public function get_min_max_width(): array
+ {
+ $fontMetrics = $this->getFontMetrics();
+ $frame = $this->_frame;
+ $style = $frame->get_style();
+ $text = $frame->get_text();
+ $font = $style->font_family;
+ $size = $style->font_size;
+ $word_spacing = $style->word_spacing;
+ $letter_spacing = $style->letter_spacing;
+
+ // Handle text transform and white space
+ $text = $this->pre_process_text($frame->get_text());
+
+ if (!$frame->is_pre()) {
+ // Determine whether the frame is at the start of its parent block.
+ // Trim leading white space in that case
+ $child = $frame;
+ $p = $frame->get_parent();
+ while (!$p->is_block() && !$child->get_prev_sibling()) {
+ $child = $p;
+ $p = $p->get_parent();
+ }
+
+ if (!$child->get_prev_sibling()) {
+ $text = ltrim($text, " ");
+ }
+
+ // Determine whether the frame is at the end of its parent block.
+ // Trim trailing white space in that case
+ $child = $frame;
+ $p = $frame->get_parent();
+ while (!$p->is_block() && !$child->get_next_sibling()) {
+ $child = $p;
+ $p = $p->get_parent();
+ }
+
+ if (!$child->get_next_sibling()) {
+ $text = rtrim($text, " ");
+ }
+ }
+
+ // Strip soft hyphens for max-line-width calculations
+ $visible_text = preg_replace('/\xAD/u', "", $text);
+
+ // Determine minimum text width
+ switch ($style->white_space) {
+ default:
+ case "normal":
+ case "pre-line":
+ case "pre-wrap":
+ // The min width is the longest word or, if breaking words is
+ // allowed with the `anywhere` keyword, the widest character.
+ // For performance reasons, we only check the first character in
+ // the latter case.
+ // https://www.w3.org/TR/css-text-3/#overflow-wrap-property
+ if ($style->overflow_wrap === "anywhere") {
+ $char = mb_substr($visible_text, 0, 1);
+ $min = $fontMetrics->getTextWidth($char, $font, $size, $word_spacing, $letter_spacing);
+ } else {
+ // Find the longest word
+ $words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $lengths = array_map(function ($chunk) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
+ // Allow trailing white space to overflow. As in actual
+ // layout above, only handle a single space for now
+ $sep = $chunk[1] ?? "";
+ $word = $sep === " " ? $chunk[0] : $chunk[0] . $sep;
+ return $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
+ }, array_chunk($words, 2));
+ $min = max($lengths);
+ }
+ break;
+
+ case "pre":
+ // Find the longest line
+ $lines = array_flip(preg_split("/\R/u", $visible_text));
+ array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
+ $chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
+ });
+ arsort($lines);
+ $min = reset($lines);
+ break;
+
+ case "nowrap":
+ $min = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
+ break;
+ }
+
+ // Determine maximum text width
+ switch ($style->white_space) {
+ default:
+ case "normal":
+ $max = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
+ break;
+
+ case "pre-line":
+ case "pre-wrap":
+ // Find the longest line
+ $lines = array_flip(preg_split("/\R/u", $visible_text));
+ array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
+ $chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
+ });
+ arsort($lines);
+ $max = reset($lines);
+ break;
+
+ case "pre":
+ case "nowrap":
+ $max = $min;
+ break;
+ }
+
+ // Account for margins, borders, and padding
+ $dims = [
+ $style->padding_left,
+ $style->padding_right,
+ $style->border_left_width,
+ $style->border_right_width,
+ $style->margin_left,
+ $style->margin_right
+ ];
+
+ // The containing block is not defined yet, treat percentages as 0
+ $delta = (float) $style->length_in_pt($dims, 0);
+ $min += $delta;
+ $max += $delta;
+
+ return [$min, $max, "min" => $min, "max" => $max];
+ }
+
+ /**
+ * @param FontMetrics $fontMetrics
+ * @return $this
+ */
+ public function setFontMetrics(FontMetrics $fontMetrics)
+ {
+ $this->fontMetrics = $fontMetrics;
+ return $this;
+ }
+
+ /**
+ * @return FontMetrics
+ */
+ public function getFontMetrics()
+ {
+ return $this->fontMetrics;
+ }
+}
diff --git a/library/vendor/dompdf/src/Helpers.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Helpers.php
similarity index 66%
rename from library/vendor/dompdf/src/Helpers.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Helpers.php
index ab05b6a74..ba78515a4 100644
--- a/library/vendor/dompdf/src/Helpers.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Helpers.php
@@ -1,4 +1,9 @@
1 && $url[0] !== '\\' && $url[1] !== ':'))) {
- // For rel path and local acess we ignore the host, and run the path through realpath()
+ // For rel path and local access we ignore the host, and run the path through realpath()
$ret .= realpath($base_path) . '/';
}
$ret .= $url;
$ret = preg_replace('/\?(.*)$/', "", $ret);
+
+ $filepath = realpath($ret);
+ if ($filepath === false) {
+ return null;
+ }
+
+ $ret = "$protocol$filepath$res";
+
return $ret;
}
+ $ret = $protocol;
// Protocol relative urls (e.g. "//example.org/style.css")
if (strpos($url, '//') === 0) {
$ret .= substr($url, 2);
@@ -96,6 +136,27 @@ class Helpers
$ret .= $host . $base_path . $url;
}
+ // URL should now be complete, final cleanup
+ $parsed_url = parse_url($ret);
+
+ // reproduced from https://www.php.net/manual/en/function.parse-url.php#106731
+ $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
+ $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
+ $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
+ $user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
+ $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
+ $pass = ($user || $pass) ? "$pass@" : '';
+ $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
+ $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
+ $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
+
+ // partially reproduced from https://stackoverflow.com/a/1243431/264628
+ /* replace '//' or '/./' or '/foo/../' with '/' */
+ $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
+ for ($n=1; $n>0; $path=preg_replace($re, '/', $path, -1, $n)) {}
+
+ $ret = "$scheme$user$pass$host$port$path$query$fragment";
+
return $ret;
}
@@ -127,27 +188,32 @@ class Helpers
}
/**
- * Converts decimal numbers to roman numerals
+ * Converts decimal numbers to roman numerals.
*
- * @param int $num
+ * As numbers larger than 3999 (and smaller than 1) cannot be represented in
+ * the standard form of roman numerals, those are left in decimal form.
+ *
+ * See https://en.wikipedia.org/wiki/Roman_numerals#Standard_form
+ *
+ * @param int|string $num
*
* @throws Exception
* @return string
*/
- public static function dec2roman($num)
+ public static function dec2roman($num): string
{
- static $ones = array("", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix");
- static $tens = array("", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc");
- static $hund = array("", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm");
- static $thou = array("", "m", "mm", "mmm");
+ static $ones = ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"];
+ static $tens = ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"];
+ static $hund = ["", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"];
+ static $thou = ["", "m", "mm", "mmm"];
if (!is_numeric($num)) {
throw new Exception("dec2roman() requires a numeric argument.");
}
- if ($num > 4000 || $num < 0) {
- return "(out of range)";
+ if ($num >= 4000 || $num <= 0) {
+ return (string) $num;
}
$num = strrev((string)$num);
@@ -173,16 +239,32 @@ class Helpers
return $ret;
}
+ /**
+ * Restrict a length to the given range.
+ *
+ * If min > max, the result is min.
+ *
+ * @param float $length
+ * @param float $min
+ * @param float $max
+ *
+ * @return float
+ */
+ public static function clamp(float $length, float $min, float $max): float
+ {
+ return max($min, min($length, $max));
+ }
+
/**
* Determines whether $value is a percentage or not
*
- * @param float $value
+ * @param string|float|int $value
*
* @return bool
*/
- public static function is_percent($value)
+ public static function is_percent($value): bool
{
- return false !== mb_strpos($value, "%");
+ return is_string($value) && false !== mb_strpos($value, "%");
}
/**
@@ -200,11 +282,11 @@ class Helpers
}
$match['data'] = rawurldecode($match['data']);
- $result = array(
+ $result = [
'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII',
'mime' => $match['mime'] ? $match['mime'] : 'text/plain',
'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'],
- );
+ ];
return $result;
}
@@ -226,18 +308,18 @@ class Helpers
* @return string The original URL with special characters encoded
*/
public static function encodeURI($uri) {
- $unescaped = array(
+ $unescaped = [
'%2D'=>'-','%5F'=>'_','%2E'=>'.','%21'=>'!', '%7E'=>'~',
'%2A'=>'*', '%27'=>"'", '%28'=>'(', '%29'=>')'
- );
- $reserved = array(
+ ];
+ $reserved = [
'%3B'=>';','%2C'=>',','%2F'=>'/','%3F'=>'?','%3A'=>':',
'%40'=>'@','%26'=>'&','%3D'=>'=','%2B'=>'+','%24'=>'$'
- );
- $score = array(
+ ];
+ $score = [
'%23'=>'#'
- );
- return strtr(rawurlencode(rawurldecode($uri)), array_merge($reserved,$unescaped,$score));
+ ];
+ return strtr(rawurlencode(rawurldecode($uri)), array_merge($reserved, $unescaped, $score));
}
/**
@@ -245,7 +327,7 @@ class Helpers
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
*
* @param string $str Data to decode
- * @param integer $width Image width
+ * @param int $width Image width
*
* @return string
*/
@@ -298,7 +380,7 @@ class Helpers
* see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
*
* @param string $str Data to decode
- * @param integer $width Image width
+ * @param int $width Image width
*
* @return string
*/
@@ -306,7 +388,7 @@ class Helpers
{
$w = floor($width / 2) + ($width % 2);
$lineWidth = $w + (3 - (($width - 1) / 2) % 4);
- $pixels = array();
+ $pixels = [];
$cnt = strlen($str);
$c = 0;
@@ -380,14 +462,14 @@ class Helpers
$host = "";
$path = "";
$file = "";
+ $res = "";
$arr = parse_url($url);
if ( isset($arr["scheme"]) ) {
$arr["scheme"] = mb_strtolower($arr["scheme"]);
}
- // Exclude windows drive letters...
- if (isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1) {
+ if (isset($arr["scheme"]) && $arr["scheme"] !== "file" && $arr["scheme"] !== "phar" && strlen($arr["scheme"]) > 1) {
$protocol = $arr["scheme"] . "://";
if (isset($arr["user"])) {
@@ -429,42 +511,32 @@ class Helpers
} else {
- $i = mb_stripos($url, "file://");
- if ($i !== false) {
- $url = mb_substr($url, $i + 7);
- }
-
- $protocol = ""; // "file://"; ? why doesn't this work... It's because of
- // network filenames like //COMPU/SHARENAME
-
+ $protocol = "";
$host = ""; // localhost, really
- $file = basename($url);
-
- $path = dirname($url);
-
- // Check that the path exists
- if ($path !== false) {
- $path .= '/';
+ $i = mb_stripos($url, "://");
+ if ($i !== false) {
+ $protocol = mb_strtolower(mb_substr($url, 0, $i + 3));
+ $url = mb_substr($url, $i + 3);
} else {
- // generate a url to access the file if no real path found.
- $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
-
- $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n");
-
- if (substr($arr["path"], 0, 1) === '/') {
- $path = dirname($arr["path"]);
- } else {
- $path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"];
- }
+ $protocol = "file://";
}
+
+ if ($protocol === "phar://") {
+ $res = substr($url, stripos($url, ".phar")+5);
+ $url = substr($url, 7, stripos($url, ".phar")-2);
+ }
+
+ $file = basename($url);
+ $path = dirname($url) . "/";
}
- $ret = array($protocol, $host, $path, $file,
+ $ret = [$protocol, $host, $path, $file,
"protocol" => $protocol,
"host" => $host,
"path" => $path,
- "file" => $file);
+ "file" => $file,
+ "resource" => $res];
return $ret;
}
@@ -503,7 +575,7 @@ class Helpers
public static function record_warnings($errno, $errstr, $errfile, $errline)
{
// Not a warning or notice
- if (!($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING))) {
+ if (!($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING | E_STRICT | E_DEPRECATED | E_USER_DEPRECATED))) {
throw new Exception($errstr . " $errno");
}
@@ -525,12 +597,12 @@ class Helpers
{
if ($c <= 0x7F) {
return chr($c);
- } else if ($c <= 0x7FF) {
+ } elseif ($c <= 0x7FF) {
return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
- } else if ($c <= 0xFFFF) {
+ } elseif ($c <= 0xFFFF) {
return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
. chr(0x80 | $c & 0x3F);
- } else if ($c <= 0x10FFFF) {
+ } elseif ($c <= 0x10FFFF) {
return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
. chr(0x80 | $c >> 6 & 0x3F)
. chr(0x80 | $c & 0x3F);
@@ -551,7 +623,7 @@ class Helpers
public static function cmyk_to_rgb($c, $m = null, $y = null, $k = null)
{
if (is_array($c)) {
- list($c, $m, $y, $k) = $c;
+ [$c, $m, $y, $k] = $c;
}
$c *= 255;
@@ -573,10 +645,10 @@ class Helpers
$b = 0;
}
- return array(
+ return [
$r, $g, $b,
"r" => $r, "g" => $g, "b" => $b
- );
+ ];
}
/**
@@ -584,50 +656,52 @@ class Helpers
*
* @param string $filename
* @param resource $context
- * @return array The same format as getimagesize($filename)
+ * @return array An array of three elements: width and height as
+ * `float|int`, and image type as `string|null`.
*/
public static function dompdf_getimagesize($filename, $context = null)
{
- static $cache = array();
+ static $cache = [];
if (isset($cache[$filename])) {
return $cache[$filename];
}
- list($width, $height, $type) = getimagesize($filename);
+ [$width, $height, $type] = getimagesize($filename);
// Custom types
- $types = array(
+ $types = [
IMAGETYPE_JPEG => "jpeg",
IMAGETYPE_GIF => "gif",
IMAGETYPE_BMP => "bmp",
IMAGETYPE_PNG => "png",
- );
+ IMAGETYPE_WEBP => "webp",
+ ];
- $type = isset($types[$type]) ? $types[$type] : null;
+ $type = $types[$type] ?? null;
if ($width == null || $height == null) {
- list($data, $headers) = Helpers::getFileContent($filename, $context);
+ [$data] = Helpers::getFileContent($filename, $context);
- if (substr($data, 0, 2) === "BM") {
- $meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data);
- $width = (int)$meta['width'];
- $height = (int)$meta['height'];
- $type = "bmp";
- }
- else {
- if (strpos($data, "loadFile($filename);
- list($width, $height) = $doc->getDimensions();
+ [$width, $height] = $doc->getDimensions();
+ $width = (float) $width;
+ $height = (float) $height;
$type = "svg";
}
}
-
}
- return $cache[$filename] = array($width, $height, $type);
+ return $cache[$filename] = [$width ?? 0, $height ?? 0, $type];
}
/**
@@ -693,7 +767,7 @@ class Helpers
$meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
// read color palette
- $palette = array();
+ $palette = [];
if ($meta['bits'] < 16) {
$palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
// in rare cases the color value is signed
@@ -815,58 +889,119 @@ class Helpers
* - curl: if allow_url_fopen is false and curl is available
*
* @param string $uri
- * @param resource $context (ignored if curl is used)
+ * @param resource $context
* @param int $offset
- * @param int $maxlen (ignored if curl is used)
- * @return bool|array
+ * @param int $maxlen
+ * @return string[]
*/
public static function getFileContent($uri, $context = null, $offset = 0, $maxlen = null)
{
- $result = false;
+ $content = null;
$headers = null;
- list($proto, $host, $path, $file) = Helpers::explode_url($uri);
- $is_local_path = ($proto == "" || $proto === "file://");
+ [$protocol] = Helpers::explode_url($uri);
+ $is_local_path = in_array(strtolower($protocol), ["", "file://", "phar://"], true);
+ $can_use_curl = in_array(strtolower($protocol), ["http://", "https://"], true);
- set_error_handler(array("\\Dompdf\\Helpers", "record_warnings"));
+ set_error_handler([self::class, 'record_warnings']);
- if ($is_local_path || ini_get("allow_url_fopen")) {
- if ($is_local_path === false) {
- $uri = Helpers::encodeURI($uri);
+ try {
+ if ($is_local_path || ini_get('allow_url_fopen') || !$can_use_curl) {
+ if ($is_local_path === false) {
+ $uri = Helpers::encodeURI($uri);
+ }
+ if (isset($maxlen)) {
+ $result = file_get_contents($uri, false, $context, $offset, $maxlen);
+ } else {
+ $result = file_get_contents($uri, false, $context, $offset);
+ }
+ if ($result !== false) {
+ $content = $result;
+ }
+ if (isset($http_response_header)) {
+ $headers = $http_response_header;
+ }
+
+ } elseif ($can_use_curl && function_exists('curl_exec')) {
+ $curl = curl_init($uri);
+
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ if ($offset > 0) {
+ curl_setopt($curl, CURLOPT_RESUME_FROM, $offset);
+ }
+
+ if ($maxlen > 0) {
+ curl_setopt($curl, CURLOPT_BUFFERSIZE, 128);
+ curl_setopt($curl, CURLOPT_NOPROGRESS, false);
+ curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, function ($res, $download_size_total, $download_size, $upload_size_total, $upload_size) use ($maxlen) {
+ return ($download_size > $maxlen) ? 1 : 0;
+ });
+ }
+
+ $context_options = [];
+ if (!is_null($context)) {
+ $context_options = stream_context_get_options($context);
+ }
+ foreach ($context_options as $stream => $options) {
+ foreach ($options as $option => $value) {
+ $key = strtolower($stream) . ":" . strtolower($option);
+ switch ($key) {
+ case "curl:curl_verify_ssl_host":
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, !$value ? 0 : 2);
+ break;
+ case "curl:max_redirects":
+ curl_setopt($curl, CURLOPT_MAXREDIRS, $value);
+ break;
+ case "http:follow_location":
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $value);
+ break;
+ case "http:header":
+ if (is_string($value)) {
+ curl_setopt($curl, CURLOPT_HTTPHEADER, [$value]);
+ } else {
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $value);
+ }
+ break;
+ case "http:timeout":
+ curl_setopt($curl, CURLOPT_TIMEOUT, $value);
+ break;
+ case "http:user_agent":
+ curl_setopt($curl, CURLOPT_USERAGENT, $value);
+ break;
+ case "curl:curl_verify_ssl_peer":
+ case "ssl:verify_peer":
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $value);
+ break;
+ }
+ }
+ }
+
+ $data = curl_exec($curl);
+
+ if ($data !== false && !curl_errno($curl)) {
+ switch ($http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE)) {
+ case 200:
+ $raw_headers = substr($data, 0, curl_getinfo($curl, CURLINFO_HEADER_SIZE));
+ $headers = preg_split("/[\n\r]+/", trim($raw_headers));
+ $content = substr($data, curl_getinfo($curl, CURLINFO_HEADER_SIZE));
+ break;
+ }
+ }
+ curl_close($curl);
}
- if (isset($maxlen)) {
- $result = file_get_contents($uri, null, $context, $offset, $maxlen);
- } else {
- $result = file_get_contents($uri, null, $context, $offset);
- }
- if (isset($http_response_header)) {
- $headers = $http_response_header;
- }
-
- } elseif (function_exists("curl_exec")) {
- $curl = curl_init($uri);
-
- //TODO: use $context to define additional curl options
- curl_setopt($curl, CURLOPT_TIMEOUT, 10);
- curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_HEADER, true);
- if ($offset > 0) {
- curl_setopt($curl, CURLOPT_RESUME_FROM, $offset);
- }
-
- $data = curl_exec($curl);
- $raw_headers = substr($data, 0, curl_getinfo($curl, CURLINFO_HEADER_SIZE));
- $headers = preg_split("/[\n\r]+/", trim($raw_headers));
- $result = substr($data, curl_getinfo($curl, CURLINFO_HEADER_SIZE));
- curl_close($curl);
+ } finally {
+ restore_error_handler();
}
- restore_error_handler();
-
- return array($result, $headers);
+ return [$content, $headers];
}
- public static function mb_ucwords($str) {
+ /**
+ * @param string $str
+ * @return string
+ */
+ public static function mb_ucwords(string $str): string
+ {
$max_len = mb_strlen($str);
if ($max_len === 1) {
return mb_strtoupper($str);
@@ -874,7 +1009,7 @@ class Helpers
$str = mb_strtoupper(mb_substr($str, 0, 1)) . mb_substr($str, 1);
- foreach (array(' ', '.', ',', '!', '?', '-', '+') as $s) {
+ foreach ([' ', '.', ',', '!', '?', '-', '+'] as $s) {
$pos = 0;
while (($pos = mb_strpos($str, $s, $pos)) !== false) {
$pos++;
@@ -892,4 +1027,67 @@ class Helpers
return $str;
}
+
+ /**
+ * Check whether two lengths should be considered equal, accounting for
+ * inaccuracies in float computation.
+ *
+ * The implementation relies on the fact that we are neither dealing with
+ * very large, nor with very small numbers in layout. Adapted from
+ * https://floating-point-gui.de/errors/comparison/.
+ *
+ * @param float $a
+ * @param float $b
+ *
+ * @return bool
+ */
+ public static function lengthEqual(float $a, float $b): bool
+ {
+ // The epsilon results in a precision of at least:
+ // * 7 decimal digits at around 1
+ // * 4 decimal digits at around 1000 (around the size of common paper formats)
+ // * 2 decimal digits at around 100,000 (100,000pt ~ 35.28m)
+ static $epsilon = 1e-8;
+ static $almostZero = 1e-12;
+
+ $diff = abs($a - $b);
+
+ if ($a === $b || $diff < $almostZero) {
+ return true;
+ }
+
+ return $diff < $epsilon * max(abs($a), abs($b));
+ }
+
+ /**
+ * Check `$a < $b`, accounting for inaccuracies in float computation.
+ */
+ public static function lengthLess(float $a, float $b): bool
+ {
+ return $a < $b && !self::lengthEqual($a, $b);
+ }
+
+ /**
+ * Check `$a <= $b`, accounting for inaccuracies in float computation.
+ */
+ public static function lengthLessOrEqual(float $a, float $b): bool
+ {
+ return $a <= $b || self::lengthEqual($a, $b);
+ }
+
+ /**
+ * Check `$a > $b`, accounting for inaccuracies in float computation.
+ */
+ public static function lengthGreater(float $a, float $b): bool
+ {
+ return $a > $b && !self::lengthEqual($a, $b);
+ }
+
+ /**
+ * Check `$a >= $b`, accounting for inaccuracies in float computation.
+ */
+ public static function lengthGreaterOrEqual(float $a, float $b): bool
+ {
+ return $a >= $b || self::lengthEqual($a, $b);
+ }
}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Image/Cache.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Image/Cache.php
new file mode 100644
index 000000000..f337bbb47
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Image/Cache.php
@@ -0,0 +1,254 @@
+getAllowedProtocols();
+ if (!array_key_exists($protocol, $allowed_protocols)) {
+ throw new ImageException("Permission denied on $url. The communication protocol is not supported.", E_WARNING);
+ }
+ foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
+ [$result, $message] = $rule($full_url);
+ if (!$result) {
+ throw new ImageException("Error loading $url: $message", E_WARNING);
+ }
+ }
+ }
+
+ if ($protocol === "file://") {
+ $resolved_url = $full_url;
+ } elseif (isset(self::$_cache[$full_url])) {
+ $resolved_url = self::$_cache[$full_url];
+ } else {
+ $tmp_dir = $options->getTempDir();
+ if (($resolved_url = @tempnam($tmp_dir, "ca_dompdf_img_")) === false) {
+ throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
+ }
+ $tempfile = $resolved_url;
+
+ $image = null;
+ if ($is_data_uri) {
+ if (($parsed_data_uri = Helpers::parse_data_uri($url)) !== false) {
+ $image = $parsed_data_uri["data"];
+ }
+ } else {
+ list($image, $http_response_header) = Helpers::getFileContent($full_url, $options->getHttpContext());
+ }
+
+ // Image not found or invalid
+ if ($image === null) {
+ $msg = ($is_data_uri ? "Data-URI could not be parsed" : "Image not found");
+ throw new ImageException($msg, E_WARNING);
+ }
+
+ if (@file_put_contents($resolved_url, $image) === false) {
+ throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
+ }
+
+ self::$_cache[$full_url] = $resolved_url;
+ }
+
+ // Check if the local file is readable
+ if (!is_readable($resolved_url) || !filesize($resolved_url)) {
+ throw new ImageException("Image not readable or empty", E_WARNING);
+ }
+
+ list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext());
+
+ if (($width && $height && in_array($type, ["gif", "png", "jpeg", "bmp", "svg","webp"], true)) === false) {
+ throw new ImageException("Image type unknown", E_WARNING);
+ }
+
+ if ($type === "svg") {
+ $parser = xml_parser_create("utf-8");
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
+ xml_set_element_handler(
+ $parser,
+ function ($parser, $name, $attributes) use ($options, $parsed_url, $full_url) {
+ if ($name === "image") {
+ $attributes = array_change_key_case($attributes, CASE_LOWER);
+ $url = $attributes["xlink:href"] ?? $attributes["href"];
+ if (!empty($url)) {
+ $inner_full_url = Helpers::build_url($parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $url);
+ if ($inner_full_url === $full_url) {
+ throw new ImageException("SVG self-reference is not allowed", E_WARNING);
+ }
+ [$resolved_url, $type, $message] = self::resolve_url($url, $parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $options);
+ if (!empty($message)) {
+ throw new ImageException("This SVG document references a restricted resource. $message", E_WARNING);
+ }
+ }
+ }
+ },
+ false
+ );
+
+ if (($fp = fopen($resolved_url, "r")) !== false) {
+ while ($line = fread($fp, 8192)) {
+ xml_parse($parser, $line, false);
+ }
+ fclose($fp);
+ }
+ xml_parser_free($parser);
+ }
+ } catch (ImageException $e) {
+ if ($tempfile) {
+ unlink($tempfile);
+ }
+ $resolved_url = self::$broken_image;
+ list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext());
+ $message = self::$error_message;
+ Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine());
+ self::$_cache[$full_url] = $resolved_url;
+ }
+
+ return [$resolved_url, $type, $message];
+ }
+
+ /**
+ * Register a temp file for the given original image file.
+ *
+ * @param string $filePath The path of the original image.
+ * @param string $tempPath The path of the temp file to register.
+ * @param string $key An optional key to register the temp file at.
+ */
+ static function addTempImage(string $filePath, string $tempPath, string $key = "default"): void
+ {
+ if (!isset(self::$tempImages[$filePath])) {
+ self::$tempImages[$filePath] = [];
+ }
+
+ self::$tempImages[$filePath][$key] = $tempPath;
+ }
+
+ /**
+ * Get the path of a temp file registered for the given original image file.
+ *
+ * @param string $filePath The path of the original image.
+ * @param string $key The key the temp file is registered at.
+ */
+ static function getTempImage(string $filePath, string $key = "default"): ?string
+ {
+ return self::$tempImages[$filePath][$key] ?? null;
+ }
+
+ /**
+ * Unlink all cached images (i.e. temporary images either downloaded
+ * or converted) except for the bundled "broken image"
+ */
+ static function clear(bool $debugPng = false)
+ {
+ foreach (self::$_cache as $file) {
+ if ($file === self::$broken_image) {
+ continue;
+ }
+ if ($debugPng) {
+ print "[clear unlink $file]";
+ }
+ if (file_exists($file)) {
+ unlink($file);
+ }
+ }
+
+ foreach (self::$tempImages as $versions) {
+ foreach ($versions as $file) {
+ if ($file === self::$broken_image) {
+ continue;
+ }
+ if ($debugPng) {
+ print "[unlink temp image $file]";
+ }
+ if (file_exists($file)) {
+ unlink($file);
+ }
+ }
+ }
+
+ self::$_cache = [];
+ self::$tempImages = [];
+ }
+
+ static function detect_type($file, $context = null)
+ {
+ list(, , $type) = Helpers::dompdf_getimagesize($file, $context);
+
+ return $type;
+ }
+
+ static function is_broken($url)
+ {
+ return $url === self::$broken_image;
+ }
+}
+
+if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.svg"))) {
+ Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.svg");
+}
diff --git a/library/vendor/dompdf/src/JavascriptEmbedder.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/JavascriptEmbedder.php
similarity index 90%
rename from library/vendor/dompdf/src/JavascriptEmbedder.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/JavascriptEmbedder.php
index 7a8fce53b..f4b9bc25a 100644
--- a/library/vendor/dompdf/src/JavascriptEmbedder.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/JavascriptEmbedder.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf;
diff --git a/library/vendor/dompdf/src/LineBox.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/LineBox.php
similarity index 64%
rename from library/vendor/dompdf/src/LineBox.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/LineBox.php
index 1634fb17d..11b83c15c 100644
--- a/library/vendor/dompdf/src/LineBox.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/LineBox.php
@@ -1,14 +1,17 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf;
+use Dompdf\FrameDecorator\AbstractFrameDecorator;
use Dompdf\FrameDecorator\Block;
+use Dompdf\FrameDecorator\ListBullet;
use Dompdf\FrameDecorator\Page;
+use Dompdf\FrameReflower\Text as TextFrameReflower;
+use Dompdf\Positioner\Inline as InlinePositioner;
/**
* The line box class
@@ -27,12 +30,17 @@ class LineBox
protected $_block_frame;
/**
- * @var Frame[]
+ * @var AbstractFrameDecorator[]
*/
- protected $_frames = array();
+ protected $_frames = [];
/**
- * @var integer
+ * @var ListBullet[]
+ */
+ protected $list_markers = [];
+
+ /**
+ * @var int
*/
public $wc = 0;
@@ -62,20 +70,27 @@ class LineBox
public $right = 0.0;
/**
- * @var Frame
+ * @var AbstractFrameDecorator
*/
public $tallest_frame = null;
/**
* @var bool[]
*/
- public $floating_blocks = array();
+ public $floating_blocks = [];
/**
* @var bool
*/
public $br = false;
+ /**
+ * Whether the line box contains any inline-positioned frames.
+ *
+ * @var bool
+ */
+ public $inline = false;
+
/**
* Class constructor
*
@@ -85,7 +100,7 @@ class LineBox
public function __construct(Block $frame, $y = 0)
{
$this->_block_frame = $frame;
- $this->_frames = array();
+ $this->_frames = [];
$this->y = $y;
$this->get_float_offsets();
@@ -124,7 +139,7 @@ class LineBox
$parent = $p;
- $childs = array();
+ $childs = [];
foreach ($floating_frames as $_floating) {
$p = $_floating->get_parent();
@@ -139,9 +154,6 @@ class LineBox
return $childs;
}
- /**
- *
- */
public function get_float_offsets()
{
static $anti_infinite_loop = 10000; // FIXME smelly hack
@@ -243,7 +255,7 @@ class LineBox
}
/**
- * @return Frame[]
+ * @return AbstractFrameDecorator[]
*/
function &get_frames()
{
@@ -251,11 +263,112 @@ class LineBox
}
/**
- * @param Frame $frame
+ * @param AbstractFrameDecorator $frame
*/
- public function add_frame(Frame $frame)
+ public function add_frame(Frame $frame): void
{
$this->_frames[] = $frame;
+
+ if ($frame->get_positioner() instanceof InlinePositioner) {
+ $this->inline = true;
+ }
+ }
+
+ /**
+ * Remove the frame at the given index and all following frames from the
+ * line.
+ *
+ * @param int $index
+ */
+ public function remove_frames(int $index): void
+ {
+ $lastIndex = count($this->_frames) - 1;
+
+ if ($index < 0 || $index > $lastIndex) {
+ return;
+ }
+
+ for ($i = $lastIndex; $i >= $index; $i--) {
+ $f = $this->_frames[$i];
+ unset($this->_frames[$i]);
+ $this->w -= $f->get_margin_width();
+ }
+
+ // Reset array indices
+ $this->_frames = array_values($this->_frames);
+
+ // Recalculate the height of the line
+ $h = 0.0;
+ $this->inline = false;
+
+ foreach ($this->_frames as $f) {
+ $h = max($h, $f->get_margin_height());
+
+ if ($f->get_positioner() instanceof InlinePositioner) {
+ $this->inline = true;
+ }
+ }
+
+ $this->h = $h;
+ }
+
+ /**
+ * Get the `outside` positioned list markers to be vertically aligned with
+ * the line box.
+ *
+ * @return ListBullet[]
+ */
+ public function get_list_markers(): array
+ {
+ return $this->list_markers;
+ }
+
+ /**
+ * Add a list marker to the line box.
+ *
+ * The list marker is only added for the purpose of vertical alignment, it
+ * is not actually added to the list of frames of the line box.
+ */
+ public function add_list_marker(ListBullet $marker): void
+ {
+ $this->list_markers[] = $marker;
+ }
+
+ /**
+ * An iterator of all list markers and inline positioned frames of the line
+ * box.
+ *
+ * @return \Iterator
+ */
+ public function frames_to_align(): \Iterator
+ {
+ yield from $this->list_markers;
+
+ foreach ($this->_frames as $frame) {
+ if ($frame->get_positioner() instanceof InlinePositioner) {
+ yield $frame;
+ }
+ }
+ }
+
+ /**
+ * Trim trailing whitespace from the line.
+ */
+ public function trim_trailing_ws(): void
+ {
+ $lastIndex = count($this->_frames) - 1;
+
+ if ($lastIndex < 0) {
+ return;
+ }
+
+ $lastFrame = $this->_frames[$lastIndex];
+ $reflower = $lastFrame->get_reflower();
+
+ if ($reflower instanceof TextFrameReflower && !$lastFrame->is_pre()) {
+ $reflower->trim_trailing_ws();
+ $this->recalculate_width();
+ }
}
/**
@@ -263,12 +376,12 @@ class LineBox
*
* @return float
*/
- public function recalculate_width()
+ public function recalculate_width(): float
{
- $width = 0;
+ $width = 0.0;
- foreach ($this->get_frames() as $frame) {
- $width += $frame->calculate_auto_width();
+ foreach ($this->_frames as $frame) {
+ $width += $frame->get_margin_width();
}
return $this->w = $width;
@@ -277,9 +390,9 @@ class LineBox
/**
* @return string
*/
- public function __toString()
+ public function __toString(): string
{
- $props = array("wc", "y", "w", "h", "left", "right", "br");
+ $props = ["wc", "y", "w", "h", "left", "right", "br"];
$s = "";
foreach ($props as $prop) {
$s .= "$prop: " . $this->$prop . "\n";
@@ -288,10 +401,6 @@ class LineBox
return $s;
}
- /*function __get($prop) {
- if (!isset($this->{"_$prop"})) return;
- return $this->{"_$prop"};
- }*/
}
/*
diff --git a/library/vendor/dompdf/src/Options.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Options.php
similarity index 76%
rename from library/vendor/dompdf/src/Options.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Options.php
index b6c7c362c..de127d2d5 100644
--- a/library/vendor/dompdf/src/Options.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Options.php
@@ -1,4 +1,9 @@
["rules" => []],
+ "http://" => ["rules" => []],
+ "https://" => ["rules" => []]
+ ];
+
/**
* @var string
*/
private $logOutputFile;
/**
- * html target media view which should be rendered into pdf.
- * List of types and parsing rules for future extensions:
- * http://www.w3.org/TR/REC-html40/types.html
- * screen, tty, tv, projection, handheld, print, braille, aural, all
- * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
- * Note, even though the generated pdf file is intended for print output,
- * the desired content might be different (e.g. screen or projection view of html file).
- * Therefore allow specification of content here.
+ * Styles targeted to this media type are applied to the document.
+ * This is on top of the media types that are always applied:
+ * all, static, visual, bitmap, paged, dompdf
*
* @var string
*/
@@ -182,28 +199,33 @@ class Options
private $isRemoteEnabled = false;
/**
- * Enable inline Javascript
+ * Enable inline JavaScript
*
* If this setting is set to true then DOMPDF will automatically insert
- * JavaScript code contained within tags.
+ * JavaScript code contained within
+ * tags as written into the PDF.
+ *
+ * NOTE: This is PDF-based JavaScript to be executed by the PDF viewer,
+ * not browser-based JavaScript executed by Dompdf.
*
* @var bool
*/
private $isJavascriptEnabled = true;
/**
- * Use the more-than-experimental HTML5 Lib parser
+ * Use the HTML5 Lib parser
*
+ * @deprecated
* @var bool
*/
- private $isHtml5ParserEnabled = false;
+ private $isHtml5ParserEnabled = true;
/**
* Whether to enable font subsetting or not.
*
* @var bool
*/
- private $isFontSubsettingEnabled = false;
+ private $isFontSubsettingEnabled = true;
/**
* @var bool
@@ -275,28 +297,43 @@ class Options
private $pdflibLicense = "";
/**
- * @var string
- * @deprecated
+ * HTTP context created with stream_context_create()
+ * Will be used for file_get_contents
+ *
+ * @link https://www.php.net/manual/context.php
+ *
+ * @var resource
*/
- private $adminUsername = "user";
-
- /**
- * @var string
- * @deprecated
- */
- private $adminPassword = "password";
+ private $httpContext;
/**
* @param array $attributes
*/
public function __construct(array $attributes = null)
{
- $this->setChroot(realpath(__DIR__ . "/../"));
- $this->setRootDir($this->getChroot());
+ $rootDir = realpath(__DIR__ . "/../");
+ $this->setChroot(array($rootDir));
+ $this->setRootDir($rootDir);
$this->setTempDir(sys_get_temp_dir());
- $this->setFontDir($this->chroot . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "fonts");
+ $this->setFontDir($rootDir . "/lib/fonts");
$this->setFontCache($this->getFontDir());
- $this->setLogOutputFile($this->getTempDir() . DIRECTORY_SEPARATOR . "log.htm");
+
+ $ver = "";
+ $versionFile = realpath(__DIR__ . '/../VERSION');
+ if (($version = file_get_contents($versionFile)) !== false) {
+ $version = trim($version);
+ if ($version !== '$Format:<%h>$') {
+ $ver = "/$version";
+ }
+ }
+ $this->setHttpContext([
+ "http" => [
+ "follow_location" => false,
+ "user_agent" => "Dompdf$ver https://github.com/dompdf/dompdf"
+ ]
+ ]);
+
+ $this->setAllowedProtocols(["file://", "http://", "https://"]);
if (null !== $attributes) {
$this->set($attributes);
@@ -311,7 +348,7 @@ class Options
public function set($attributes, $value = null)
{
if (!is_array($attributes)) {
- $attributes = array($attributes => $value);
+ $attributes = [$attributes => $value];
}
foreach ($attributes as $key => $value) {
if ($key === 'tempDir' || $key === 'temp_dir') {
@@ -322,6 +359,8 @@ class Options
$this->setFontCache($value);
} elseif ($key === 'chroot') {
$this->setChroot($value);
+ } elseif ($key === 'allowedProtocols') {
+ $this->setAllowedProtocols($value);
} elseif ($key === 'logOutputFile' || $key === 'log_output_file') {
$this->setLogOutputFile($value);
} elseif ($key === 'defaultMediaType' || $key === 'default_media_type') {
@@ -366,10 +405,8 @@ class Options
$this->setPdfBackend($value);
} elseif ($key === 'pdflibLicense' || $key === 'pdflib_license') {
$this->setPdflibLicense($value);
- } elseif ($key === 'adminUsername' || $key === 'admin_username') {
- $this->setAdminUsername($value);
- } elseif ($key === 'adminPassword' || $key === 'admin_password') {
- $this->setAdminPassword($value);
+ } elseif ($key === 'httpContext' || $key === 'http_context') {
+ $this->setHttpContext($value);
}
}
return $this;
@@ -389,6 +426,8 @@ class Options
return $this->getFontCache();
} elseif ($key === 'chroot') {
return $this->getChroot();
+ } elseif ($key === 'allowedProtocols') {
+ return $this->getAllowedProtocols();
} elseif ($key === 'logOutputFile' || $key === 'log_output_file') {
return $this->getLogOutputFile();
} elseif ($key === 'defaultMediaType' || $key === 'default_media_type') {
@@ -433,50 +472,12 @@ class Options
return $this->getPdfBackend();
} elseif ($key === 'pdflibLicense' || $key === 'pdflib_license') {
return $this->getPdflibLicense();
- } elseif ($key === 'adminUsername' || $key === 'admin_username') {
- return $this->getAdminUsername();
- } elseif ($key === 'adminPassword' || $key === 'admin_password') {
- return $this->getAdminPassword();
+ } elseif ($key === 'httpContext' || $key === 'http_context') {
+ return $this->getHttpContext();
}
return null;
}
- /**
- * @param string $adminPassword
- * @return $this
- */
- public function setAdminPassword($adminPassword)
- {
- $this->adminPassword = $adminPassword;
- return $this;
- }
-
- /**
- * @return string
- */
- public function getAdminPassword()
- {
- return $this->adminPassword;
- }
-
- /**
- * @param string $adminUsername
- * @return $this
- */
- public function setAdminUsername($adminUsername)
- {
- $this->adminUsername = $adminUsername;
- return $this;
- }
-
- /**
- * @return string
- */
- public function getAdminUsername()
- {
- return $this->adminUsername;
- }
-
/**
* @param string $pdfBackend
* @return $this
@@ -514,21 +515,93 @@ class Options
}
/**
- * @param string $chroot
+ * @param array|string $chroot
* @return $this
*/
- public function setChroot($chroot)
+ public function setChroot($chroot, $delimiter = ',')
{
- $this->chroot = $chroot;
+ if (is_string($chroot)) {
+ $this->chroot = explode($delimiter, $chroot);
+ } elseif (is_array($chroot)) {
+ $this->chroot = $chroot;
+ }
return $this;
}
/**
- * @return string
+ * @return array
+ */
+ public function getAllowedProtocols()
+ {
+ return $this->allowedProtocols;
+ }
+
+ /**
+ * @param array $allowedProtocols The protocols to allow, as an array
+ * formatted as ["protocol://" => ["rules" => [callable]], ...]
+ * or ["protocol://", ...]
+ *
+ * @return $this
+ */
+ public function setAllowedProtocols(array $allowedProtocols)
+ {
+ $protocols = [];
+ foreach ($allowedProtocols as $protocol => $config) {
+ if (is_string($protocol)) {
+ $protocols[$protocol] = [];
+ if (is_array($config)) {
+ $protocols[$protocol] = $config;
+ }
+ } elseif (is_string($config)) {
+ $protocols[$config] = [];
+ }
+ }
+ $this->allowedProtocols = [];
+ foreach ($protocols as $protocol => $config) {
+ $this->addAllowedProtocol($protocol, ...($config["rules"] ?? []));
+ }
+ return $this;
+ }
+
+ /**
+ * Adds a new protocol to the allowed protocols collection
+ *
+ * @param string $protocol The scheme to add (e.g. "http://")
+ * @param callable $rule A callable that validates the protocol
+ * @return $this
+ */
+ public function addAllowedProtocol(string $protocol, callable ...$rules)
+ {
+ $protocol = strtolower($protocol);
+ if (empty($rules)) {
+ $rules = [];
+ switch ($protocol) {
+ case "file://":
+ $rules[] = [$this, "validateLocalUri"];
+ break;
+ case "http://":
+ case "https://":
+ $rules[] = [$this, "validateRemoteUri"];
+ break;
+ case "phar://":
+ $rules[] = [$this, "validatePharUri"];
+ break;
+ }
+ }
+ $this->allowedProtocols[$protocol] = ["rules" => $rules];
+ return $this;
+ }
+
+ /**
+ * @return array
*/
public function getChroot()
{
- return $this->chroot;
+ $chroot = [];
+ if (is_array($this->chroot)) {
+ $chroot = $this->chroot;
+ }
+ return $chroot;
}
/**
@@ -681,7 +754,11 @@ class Options
*/
public function setDefaultFont($defaultFont)
{
- $this->defaultFont = $defaultFont;
+ if (!($defaultFont === null || trim($defaultFont) === "")) {
+ $this->defaultFont = $defaultFont;
+ } else {
+ $this->defaultFont = "serif";
+ }
return $this;
}
@@ -846,6 +923,7 @@ class Options
}
/**
+ * @deprecated
* @param boolean $isHtml5ParserEnabled
* @return $this
*/
@@ -856,6 +934,7 @@ class Options
}
/**
+ * @deprecated
* @return boolean
*/
public function getIsHtml5ParserEnabled()
@@ -864,6 +943,7 @@ class Options
}
/**
+ * @deprecated
* @return boolean
*/
public function isHtml5ParserEnabled()
@@ -1002,4 +1082,78 @@ class Options
{
return $this->rootDir;
}
-}
\ No newline at end of file
+
+ /**
+ * Sets the HTTP context
+ *
+ * @param resource|array $httpContext
+ * @return $this
+ */
+ public function setHttpContext($httpContext)
+ {
+ $this->httpContext = is_array($httpContext) ? stream_context_create($httpContext) : $httpContext;
+ return $this;
+ }
+
+ /**
+ * Returns the HTTP context
+ *
+ * @return resource
+ */
+ public function getHttpContext()
+ {
+ return $this->httpContext;
+ }
+
+ public function validateLocalUri(string $uri)
+ {
+ if ($uri === null || strlen($uri) === 0) {
+ return [false, "The URI must not be empty."];
+ }
+
+ $realfile = realpath(str_replace("file://", "", $uri));
+
+ $dirs = $this->chroot;
+ $dirs[] = $this->rootDir;
+ $chrootValid = false;
+ foreach ($dirs as $chrootPath) {
+ $chrootPath = realpath($chrootPath);
+ if ($chrootPath !== false && strpos($realfile, $chrootPath) === 0) {
+ $chrootValid = true;
+ break;
+ }
+ }
+ if ($chrootValid !== true) {
+ return [false, "Permission denied. The file could not be found under the paths specified by Options::chroot."];
+ }
+
+ if (!$realfile) {
+ return [false, "File not found."];
+ }
+
+ return [true, null];
+ }
+
+ public function validatePharUri(string $uri)
+ {
+ if ($uri === null || strlen($uri) === 0) {
+ return [false, "The URI must not be empty."];
+ }
+
+ $file = substr(substr($uri, 0, strpos($uri, ".phar") + 5), 7);
+ return $this->validateLocalUri($file);
+ }
+
+ public function validateRemoteUri(string $uri)
+ {
+ if ($uri === null || strlen($uri) === 0) {
+ return [false, "The URI must not be empty."];
+ }
+
+ if (!$this->isRemoteEnabled) {
+ return [false, "Remote file requested, but remote file download is disabled."];
+ }
+
+ return [true, null];
+ }
+}
diff --git a/library/vendor/dompdf/src/PhpEvaluator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/PhpEvaluator.php
similarity index 89%
rename from library/vendor/dompdf/src/PhpEvaluator.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/PhpEvaluator.php
index 0058027a0..4a46555ca 100644
--- a/library/vendor/dompdf/src/PhpEvaluator.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/PhpEvaluator.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf;
@@ -33,7 +32,7 @@ class PhpEvaluator
* @param $code
* @param array $vars
*/
- public function evaluate($code, $vars = array())
+ public function evaluate($code, $vars = [])
{
if (!$this->_canvas->get_dompdf()->getOptions()->getIsPhpEnabled()) {
return;
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Absolute.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Absolute.php
new file mode 100644
index 000000000..2df9a742b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Absolute.php
@@ -0,0 +1,128 @@
+get_reflower() instanceof Block) {
+ $style = $frame->get_style();
+ [$cbx, $cby, $cbw, $cbh] = $frame->get_containing_block();
+
+ // If the `top` value is `auto`, the frame will be repositioned
+ // after its height has been resolved
+ $left = (float) $style->length_in_pt($style->left, $cbw);
+ $top = (float) $style->length_in_pt($style->top, $cbh);
+
+ $frame->set_position($cbx + $left, $cby + $top);
+ } else {
+ // Legacy positioning logic for image and table frames
+ // TODO: Resolve dimensions, margins, and offsets similar to the
+ // block case in the reflowers and use the simplified logic above
+ $style = $frame->get_style();
+ $block_parent = $frame->find_block_parent();
+ $current_line = $block_parent->get_current_line_box();
+
+ list($x, $y, $w, $h) = $frame->get_containing_block();
+ $inflow_x = $block_parent->get_content_box()["x"] + $current_line->left + $current_line->w;
+ $inflow_y = $current_line->y;
+
+ $top = $style->length_in_pt($style->top, $h);
+ $right = $style->length_in_pt($style->right, $w);
+ $bottom = $style->length_in_pt($style->bottom, $h);
+ $left = $style->length_in_pt($style->left, $w);
+
+ list($width, $height) = [$frame->get_margin_width(), $frame->get_margin_height()];
+
+ $orig_width = $style->get_specified("width");
+ $orig_height = $style->get_specified("height");
+
+ /****************************
+ *
+ * Width auto:
+ * ____________| left=auto | left=fixed |
+ * right=auto | A | B |
+ * right=fixed | C | D |
+ *
+ * Width fixed:
+ * ____________| left=auto | left=fixed |
+ * right=auto | E | F |
+ * right=fixed | G | H |
+ *****************************/
+
+ if ($left === "auto") {
+ if ($right === "auto") {
+ // A or E - Keep the frame at the same position
+ $x = $inflow_x;
+ } else {
+ if ($orig_width === "auto") {
+ // C
+ $x += $w - $width - $right;
+ } else {
+ // G
+ $x += $w - $width - $right;
+ }
+ }
+ } else {
+ if ($right === "auto") {
+ // B or F
+ $x += (float)$left;
+ } else {
+ if ($orig_width === "auto") {
+ // D - TODO change width
+ $x += (float)$left;
+ } else {
+ // H - Everything is fixed: left + width win
+ $x += (float)$left;
+ }
+ }
+ }
+
+ // The same vertically
+ if ($top === "auto") {
+ if ($bottom === "auto") {
+ // A or E - Keep the frame at the same position
+ $y = $inflow_y;
+ } else {
+ if ($orig_height === "auto") {
+ // C
+ $y += (float)$h - $height - (float)$bottom;
+ } else {
+ // G
+ $y += (float)$h - $height - (float)$bottom;
+ }
+ }
+ } else {
+ if ($bottom === "auto") {
+ // B or F
+ $y += (float)$top;
+ } else {
+ if ($orig_height === "auto") {
+ // D - TODO change height
+ $y += (float)$top;
+ } else {
+ // H - Everything is fixed: top + height win
+ $y += (float)$top;
+ }
+ }
+ }
+
+ $frame->set_position($x, $y);
+ }
+ }
+}
diff --git a/library/vendor/dompdf/src/Positioner/AbstractPositioner.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/AbstractPositioner.php
similarity index 57%
rename from library/vendor/dompdf/src/Positioner/AbstractPositioner.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/AbstractPositioner.php
index 2ade6afcb..a75c09fb9 100644
--- a/library/vendor/dompdf/src/Positioner/AbstractPositioner.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/AbstractPositioner.php
@@ -1,11 +1,9 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf\Positioner;
use Dompdf\FrameDecorator\AbstractFrameDecorator;
@@ -13,9 +11,8 @@ use Dompdf\FrameDecorator\AbstractFrameDecorator;
/**
* Base AbstractPositioner class
*
- * Defines postioner interface
+ * Defines positioner interface
*
- * @access private
* @package dompdf
*/
abstract class AbstractPositioner
@@ -23,19 +20,22 @@ abstract class AbstractPositioner
/**
* @param AbstractFrameDecorator $frame
- * @return mixed
*/
- abstract function position(AbstractFrameDecorator $frame);
+ abstract function position(AbstractFrameDecorator $frame): void;
/**
* @param AbstractFrameDecorator $frame
- * @param $offset_x
- * @param $offset_y
- * @param bool $ignore_self
+ * @param float $offset_x
+ * @param float $offset_y
+ * @param bool $ignore_self
*/
- function move(AbstractFrameDecorator $frame, $offset_x, $offset_y, $ignore_self = false)
- {
- list($x, $y) = $frame->get_position();
+ function move(
+ AbstractFrameDecorator $frame,
+ float $offset_x,
+ float $offset_y,
+ bool $ignore_self = false
+ ): void {
+ [$x, $y] = $frame->get_position();
if (!$ignore_self) {
$frame->set_position($x + $offset_x, $y + $offset_y);
diff --git a/library/vendor/dompdf/src/Positioner/Block.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Block.php
similarity index 52%
rename from library/vendor/dompdf/src/Positioner/Block.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Block.php
index 0c340bc8c..e6f65eab9 100644
--- a/library/vendor/dompdf/src/Positioner/Block.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Block.php
@@ -1,11 +1,9 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf\Positioner;
use Dompdf\FrameDecorator\AbstractFrameDecorator;
@@ -13,12 +11,12 @@ use Dompdf\FrameDecorator\AbstractFrameDecorator;
/**
* Positions block frames
*
- * @access private
* @package dompdf
*/
-class Block extends AbstractPositioner {
+class Block extends AbstractPositioner
+{
- function position(AbstractFrameDecorator $frame)
+ function position(AbstractFrameDecorator $frame): void
{
$style = $frame->get_style();
$cb = $frame->get_containing_block();
@@ -31,24 +29,12 @@ class Block extends AbstractPositioner {
$p->add_line(true);
}
$y = $p->get_current_line_box()->y;
-
} else {
$y = $cb["y"];
}
$x = $cb["x"];
- // Relative positionning
- if ($style->position === "relative") {
- $top = (float)$style->length_in_pt($style->top, $cb["h"]);
- //$right = (float)$style->length_in_pt($style->right, $cb["w"]);
- //$bottom = (float)$style->length_in_pt($style->bottom, $cb["h"]);
- $left = (float)$style->length_in_pt($style->left, $cb["w"]);
-
- $x += $left;
- $y += $top;
- }
-
$frame->set_position($x, $y);
}
}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Fixed.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Fixed.php
new file mode 100644
index 000000000..13eb9e9c0
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Fixed.php
@@ -0,0 +1,92 @@
+get_reflower() instanceof Block) {
+ parent::position($frame);
+ } else {
+ // Legacy positioning logic for image and table frames
+ // TODO: Resolve dimensions, margins, and offsets similar to the
+ // block case in the reflowers and use the simplified logic above
+ $style = $frame->get_style();
+ $root = $frame->get_root();
+ $initialcb = $root->get_containing_block();
+ $initialcb_style = $root->get_style();
+
+ $p = $frame->find_block_parent();
+ if ($p) {
+ $p->add_line();
+ }
+ // Compute the margins of the @page style
+ $margin_top = (float)$initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]);
+ $margin_right = (float)$initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]);
+ $margin_bottom = (float)$initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]);
+ $margin_left = (float)$initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]);
+
+ // The needed computed style of the element
+ $height = (float)$style->length_in_pt($style->get_specified("height"), $initialcb["h"]);
+ $width = (float)$style->length_in_pt($style->get_specified("width"), $initialcb["w"]);
+
+ $top = $style->length_in_pt($style->get_specified("top"), $initialcb["h"]);
+ $right = $style->length_in_pt($style->get_specified("right"), $initialcb["w"]);
+ $bottom = $style->length_in_pt($style->get_specified("bottom"), $initialcb["h"]);
+ $left = $style->length_in_pt($style->get_specified("left"), $initialcb["w"]);
+
+ $y = $margin_top;
+ if (isset($top)) {
+ $y = (float)$top + $margin_top;
+ if ($top === "auto") {
+ $y = $margin_top;
+ if (isset($bottom) && $bottom !== "auto") {
+ $y = $initialcb["h"] - $bottom - $margin_bottom;
+ if ($frame->is_auto_height()) {
+ $y -= $height;
+ } else {
+ $y -= $frame->get_margin_height();
+ }
+ }
+ }
+ }
+
+ $x = $margin_left;
+ if (isset($left)) {
+ $x = (float)$left + $margin_left;
+ if ($left === "auto") {
+ $x = $margin_left;
+ if (isset($right) && $right !== "auto") {
+ $x = $initialcb["w"] - $right - $margin_right;
+ if ($frame->is_auto_width()) {
+ $x -= $width;
+ } else {
+ $x -= $frame->get_margin_width();
+ }
+ }
+ }
+ }
+
+ $frame->set_position($x, $y);
+
+ foreach ($frame->get_children() as $child) {
+ $child->set_position($x, $y);
+ }
+ }
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Inline.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Inline.php
new file mode 100644
index 000000000..c0bdaf263
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Inline.php
@@ -0,0 +1,52 @@
+find_block_parent();
+
+ if (!$block) {
+ throw new Exception("No block-level parent found. Not good.");
+ }
+
+ $cb = $frame->get_containing_block();
+ $line = $block->get_current_line_box();
+
+ if (!$frame->is_text_node() && !($frame instanceof InlineFrameDecorator)) {
+ // Atomic inline boxes and replaced inline elements
+ // (inline-block, inline-table, img etc.)
+ $width = $frame->get_margin_width();
+ $available_width = $cb["w"] - $line->left - $line->w - $line->right;
+
+ if (Helpers::lengthGreater($width, $available_width)) {
+ $block->add_line();
+ $line = $block->get_current_line_box();
+ }
+ }
+
+ $frame->set_position($cb["x"] + $line->w, $line->y);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/ListBullet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/ListBullet.php
new file mode 100644
index 000000000..081d59419
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/ListBullet.php
@@ -0,0 +1,42 @@
+get_parent();
+ $style = $parent->get_style();
+ $cbw = $parent->get_containing_block("w");
+ $margin_left = (float) $style->length_in_pt($style->margin_left, $cbw);
+ $border_edge = $parent->get_position("x") + $margin_left;
+
+ // This includes the marker indentation
+ $x = $border_edge - $frame->get_margin_width();
+
+ // The marker is later vertically aligned with the corresponding line
+ // box and its vertical position is fine-tuned in the renderer
+ $p = $frame->find_block_parent();
+ $y = $p->get_current_line_box()->y;
+
+ $frame->set_position($x, $y);
+ }
+}
diff --git a/library/vendor/dompdf/src/Positioner/NullPositioner.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/NullPositioner.php
similarity index 72%
rename from library/vendor/dompdf/src/Positioner/NullPositioner.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/NullPositioner.php
index afdef1901..6ad425c34 100644
--- a/library/vendor/dompdf/src/Positioner/NullPositioner.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/NullPositioner.php
@@ -1,11 +1,9 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf\Positioner;
use Dompdf\FrameDecorator\AbstractFrameDecorator;
@@ -21,7 +19,7 @@ class NullPositioner extends AbstractPositioner
/**
* @param AbstractFrameDecorator $frame
*/
- function position(AbstractFrameDecorator $frame)
+ function position(AbstractFrameDecorator $frame): void
{
return;
}
diff --git a/library/vendor/dompdf/src/Positioner/TableCell.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableCell.php
similarity index 79%
rename from library/vendor/dompdf/src/Positioner/TableCell.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableCell.php
index 42b0042e4..1a6ac6298 100644
--- a/library/vendor/dompdf/src/Positioner/TableCell.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableCell.php
@@ -1,11 +1,9 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf\Positioner;
use Dompdf\FrameDecorator\AbstractFrameDecorator;
@@ -22,7 +20,7 @@ class TableCell extends AbstractPositioner
/**
* @param AbstractFrameDecorator $frame
*/
- function position(AbstractFrameDecorator $frame)
+ function position(AbstractFrameDecorator $frame): void
{
$table = Table::find_parent_table($frame);
$cellmap = $table->get_cellmap();
diff --git a/library/vendor/dompdf/src/Positioner/TableRow.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableRow.php
similarity index 81%
rename from library/vendor/dompdf/src/Positioner/TableRow.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableRow.php
index aee20453f..79c0fcf25 100644
--- a/library/vendor/dompdf/src/Positioner/TableRow.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableRow.php
@@ -1,11 +1,9 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
-
namespace Dompdf\Positioner;
use Dompdf\FrameDecorator\AbstractFrameDecorator;
@@ -21,7 +19,7 @@ class TableRow extends AbstractPositioner
/**
* @param AbstractFrameDecorator $frame
*/
- function position(AbstractFrameDecorator $frame)
+ function position(AbstractFrameDecorator $frame): void
{
$cb = $frame->get_containing_block();
$p = $frame->get_prev_sibling();
diff --git a/library/vendor/dompdf/src/Renderer.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer.php
similarity index 82%
rename from library/vendor/dompdf/src/Renderer.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer.php
index aa3bf2f88..e3cacc104 100644
--- a/library/vendor/dompdf/src/Renderer.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf;
@@ -65,19 +64,21 @@ class Renderer extends AbstractRenderer
$style = $frame->get_style();
- if (in_array($style->visibility, array("hidden", "collapse"))) {
+ if (in_array($style->visibility, ["hidden", "collapse"], true)) {
return;
}
$display = $style->display;
+ $transformList = $style->transform;
+ $hasTransform = $transformList !== [];
// Starts the CSS transformation
- if ($style->transform && is_array($style->transform)) {
+ if ($hasTransform) {
$this->_canvas->save();
list($x, $y) = $frame->get_padding_box();
$origin = $style->transform_origin;
- foreach ($style->transform as $transform) {
+ foreach ($transformList as $transform) {
list($function, $values) = $transform;
if ($function === "matrix") {
$function = "transform";
@@ -87,7 +88,7 @@ class Renderer extends AbstractRenderer
$values[] = $x + (float)$style->length_in_pt($origin[0], (float)$style->length_in_pt($style->width));
$values[] = $y + (float)$style->length_in_pt($origin[1], (float)$style->length_in_pt($style->height));
- call_user_func_array(array($this->_canvas, $function), $values);
+ call_user_func_array([$this->_canvas, $function], $values);
}
}
@@ -154,23 +155,23 @@ class Renderer extends AbstractRenderer
// Starts the overflow: hidden box
if ($style->overflow === "hidden") {
- list($x, $y, $w, $h) = $frame->get_padding_box();
-
- // get border radii
+ $padding_box = $frame->get_padding_box();
+ [$x, $y, $w, $h] = $padding_box;
$style = $frame->get_style();
- list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
- if ($tl + $tr + $br + $bl > 0) {
- $this->_canvas->clipping_roundrectangle($x, $y, (float)$w, (float)$h, $tl, $tr, $br, $bl);
+ if ($style->has_border_radius()) {
+ $border_box = $frame->get_border_box();
+ [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box, $padding_box);
+ $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
} else {
- $this->_canvas->clipping_rectangle($x, $y, (float)$w, (float)$h);
+ $this->_canvas->clipping_rectangle($x, $y, $w, $h);
}
}
- $stack = array();
+ $stack = [];
foreach ($frame->get_children() as $child) {
- // < 0 : nagative z-index
+ // < 0 : negative z-index
// = 0 : no z-index, no stacking context
// = 1 : stacking context without z-index
// > 1 : z-index
@@ -179,8 +180,8 @@ class Renderer extends AbstractRenderer
$z_index = 0;
if ($child_z_index !== "auto") {
- $z_index = intval($child_z_index) + 1;
- } elseif ($child_style->float !== "none" || $child->is_positionned()) {
+ $z_index = $child_z_index + 1;
+ } elseif ($child_style->float !== "none" || $child->is_positioned()) {
$z_index = 1;
}
@@ -200,7 +201,7 @@ class Renderer extends AbstractRenderer
$this->_canvas->clipping_end();
}
- if ($style->transform && is_array($style->transform)) {
+ if ($hasTransform) {
$this->_canvas->restore();
}
@@ -212,27 +213,22 @@ class Renderer extends AbstractRenderer
* Check for callbacks that need to be performed when a given event
* gets triggered on a frame
*
- * @param string $event the type of event
- * @param Frame $frame the frame that event is triggered on
+ * @param string $event The type of event
+ * @param Frame $frame The frame that event is triggered on
*/
- protected function _check_callbacks($event, $frame)
+ protected function _check_callbacks(string $event, Frame $frame): void
{
if (!isset($this->_callbacks)) {
$this->_callbacks = $this->_dompdf->getCallbacks();
}
- if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
- $info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
- 1 => $frame, "frame" => $frame);
+ if (isset($this->_callbacks[$event])) {
$fs = $this->_callbacks[$event];
+ $canvas = $this->_canvas;
+ $fontMetrics = $this->_dompdf->getFontMetrics();
+
foreach ($fs as $f) {
- if (is_callable($f)) {
- if (is_array($f)) {
- $f[0]->{$f[1]}($info);
- } else {
- $f($info);
- }
- }
+ $f($frame, $canvas, $fontMetrics);
}
}
}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/AbstractRenderer.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/AbstractRenderer.php
new file mode 100644
index 000000000..8b01ef8c0
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/AbstractRenderer.php
@@ -0,0 +1,1244 @@
+_dompdf = $dompdf;
+ $this->_canvas = $dompdf->getCanvas();
+ }
+
+ /**
+ * Render a frame.
+ *
+ * Specialized in child classes
+ *
+ * @param Frame $frame The frame to render
+ */
+ abstract function render(Frame $frame);
+
+ /**
+ * @param Frame $frame
+ * @param float[] $border_box
+ */
+ protected function _render_background(Frame $frame, array $border_box): void
+ {
+ $style = $frame->get_style();
+ $color = $style->background_color;
+ $image = $style->background_image;
+ [$x, $y, $w, $h] = $border_box;
+
+ if ($color === "transparent" && $image === "none") {
+ return;
+ }
+
+ if ($style->has_border_radius()) {
+ [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box);
+ $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
+ }
+
+ if ($color !== "transparent") {
+ $this->_canvas->filled_rectangle($x, $y, $w, $h, $color);
+ }
+
+ if ($image !== "none") {
+ $this->_background_image($image, $x, $y, $w, $h, $style);
+ }
+
+ if ($style->has_border_radius()) {
+ $this->_canvas->clipping_end();
+ }
+ }
+
+ /**
+ * @param Frame $frame
+ * @param float[] $border_box
+ * @param string $corner_style
+ */
+ protected function _render_border(Frame $frame, array $border_box, string $corner_style = "bevel"): void
+ {
+ $style = $frame->get_style();
+ $bp = $style->get_border_properties();
+ [$x, $y, $w, $h] = $border_box;
+ [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box);
+
+ // Short-cut: If all the borders are "solid" with the same color and
+ // style, and no radius, we'd better draw a rectangle
+ if ($bp["top"]["style"] === "solid" &&
+ $bp["top"] === $bp["right"] &&
+ $bp["right"] === $bp["bottom"] &&
+ $bp["bottom"] === $bp["left"] &&
+ !$style->has_border_radius()
+ ) {
+ $props = $bp["top"];
+ if ($props["color"] === "transparent" || $props["width"] <= 0) {
+ return;
+ }
+
+ $width = (float)$style->length_in_pt($props["width"]);
+ $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width);
+ return;
+ }
+
+ // Do it the long way
+ $widths = [
+ (float)$style->length_in_pt($bp["top"]["width"]),
+ (float)$style->length_in_pt($bp["right"]["width"]),
+ (float)$style->length_in_pt($bp["bottom"]["width"]),
+ (float)$style->length_in_pt($bp["left"]["width"])
+ ];
+
+ foreach ($bp as $side => $props) {
+ if ($props["style"] === "none" ||
+ $props["style"] === "hidden" ||
+ $props["color"] === "transparent" ||
+ $props["width"] <= 0
+ ) {
+ continue;
+ }
+
+ [$x, $y, $w, $h] = $border_box;
+ $method = "_border_" . $props["style"];
+
+ switch ($side) {
+ case "top":
+ $length = $w;
+ $r1 = $tl;
+ $r2 = $tr;
+ break;
+
+ case "bottom":
+ $length = $w;
+ $y += $h;
+ $r1 = $bl;
+ $r2 = $br;
+ break;
+
+ case "left":
+ $length = $h;
+ $r1 = $tl;
+ $r2 = $bl;
+ break;
+
+ case "right":
+ $length = $h;
+ $x += $w;
+ $r1 = $tr;
+ $r2 = $br;
+ break;
+
+ default:
+ break;
+ }
+
+ // draw rounded corners
+ $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2);
+ }
+ }
+
+ /**
+ * @param Frame $frame
+ * @param float[] $border_box
+ * @param string $corner_style
+ */
+ protected function _render_outline(Frame $frame, array $border_box, string $corner_style = "bevel"): void
+ {
+ $style = $frame->get_style();
+
+ $width = $style->outline_width;
+ $outline_style = $style->outline_style;
+ $color = $style->outline_color;
+
+ if ($outline_style === "none" || $color === "transparent" || $width <= 0) {
+ return;
+ }
+
+ $offset = $style->outline_offset;
+
+ [$x, $y, $w, $h] = $border_box;
+ $d = $width + $offset;
+ $outline_box = [$x - $d, $y - $d, $w + $d * 2, $h + $d * 2];
+ [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box, $outline_box);
+
+ $x -= $offset;
+ $y -= $offset;
+ $w += $offset * 2;
+ $h += $offset * 2;
+
+ // For a simple outline, we can draw a rectangle
+ if ($outline_style === "solid" && !$style->has_border_radius()) {
+ $x -= $width / 2;
+ $y -= $width / 2;
+ $w += $width;
+ $h += $width;
+
+ $this->_canvas->rectangle($x, $y, $w, $h, $color, $width);
+ return;
+ }
+
+ $x -= $width;
+ $y -= $width;
+ $w += $width * 2;
+ $h += $width * 2;
+
+ $method = "_border_" . $outline_style;
+ $widths = array_fill(0, 4, $width);
+ $sides = ["top", "right", "left", "bottom"];
+
+ foreach ($sides as $side) {
+ switch ($side) {
+ case "top":
+ $length = $w;
+ $side_x = $x;
+ $side_y = $y;
+ $r1 = $tl;
+ $r2 = $tr;
+ break;
+
+ case "bottom":
+ $length = $w;
+ $side_x = $x;
+ $side_y = $y + $h;
+ $r1 = $bl;
+ $r2 = $br;
+ break;
+
+ case "left":
+ $length = $h;
+ $side_x = $x;
+ $side_y = $y;
+ $r1 = $tl;
+ $r2 = $bl;
+ break;
+
+ case "right":
+ $length = $h;
+ $side_x = $x + $w;
+ $side_y = $y;
+ $r1 = $tr;
+ $r2 = $br;
+ break;
+
+ default:
+ break;
+ }
+
+ $this->$method($side_x, $side_y, $length, $color, $widths, $side, $corner_style, $r1, $r2);
+ }
+ }
+
+ /**
+ * Render a background image over a rectangular area
+ *
+ * @param string $url The background image to load
+ * @param float $x The left edge of the rectangular area
+ * @param float $y The top edge of the rectangular area
+ * @param float $width The width of the rectangular area
+ * @param float $height The height of the rectangular area
+ * @param Style $style The associated Style object
+ *
+ * @throws \Exception
+ */
+ protected function _background_image($url, $x, $y, $width, $height, $style)
+ {
+ if (!function_exists("imagecreatetruecolor")) {
+ throw new \Exception("The PHP GD extension is required, but is not installed.");
+ }
+
+ $sheet = $style->get_stylesheet();
+
+ // Skip degenerate cases
+ if ($width == 0 || $height == 0) {
+ return;
+ }
+
+ $box_width = $width;
+ $box_height = $height;
+
+ //debugpng
+ if ($this->_dompdf->getOptions()->getDebugPng()) {
+ print '[_background_image ' . $url . ']';
+ }
+
+ list($img, $type, /*$msg*/) = Cache::resolve_url(
+ $url,
+ $sheet->get_protocol(),
+ $sheet->get_host(),
+ $sheet->get_base_path(),
+ $this->_dompdf->getOptions()
+ );
+
+ // Bail if the image is no good
+ if (Cache::is_broken($img)) {
+ return;
+ }
+
+ //Try to optimize away reading and composing of same background multiple times
+ //Postponing read with imagecreatefrom ...()
+ //final composition parameters and name not known yet
+ //Therefore read dimension directly from file, instead of creating gd object first.
+ //$img_w = imagesx($src); $img_h = imagesy($src);
+
+ list($img_w, $img_h) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext());
+ if ($img_w == 0 || $img_h == 0) {
+ return;
+ }
+
+ // save for later check if file needs to be resized.
+ $org_img_w = $img_w;
+ $org_img_h = $img_h;
+
+ $repeat = $style->background_repeat;
+ $dpi = $this->_dompdf->getOptions()->getDpi();
+
+ //Increase background resolution and dependent box size according to image resolution to be placed in
+ //Then image can be copied in without resize
+ $bg_width = round((float)($width * $dpi) / 72);
+ $bg_height = round((float)($height * $dpi) / 72);
+
+ list($img_w, $img_h) = $this->_resize_background_image(
+ $img_w,
+ $img_h,
+ $bg_width,
+ $bg_height,
+ $style->background_size,
+ $dpi
+ );
+ //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel
+
+ list($bg_x, $bg_y) = $style->background_position;
+
+ if (Helpers::is_percent($bg_x)) {
+ // The point $bg_x % from the left edge of the image is placed
+ // $bg_x % from the left edge of the background rectangle
+ $p = ((float)$bg_x) / 100.0;
+ $x1 = $p * $img_w;
+ $x2 = $p * $bg_width;
+
+ $bg_x = $x2 - $x1;
+ } else {
+ $bg_x = (float)($style->length_in_pt($bg_x) * $dpi) / 72;
+ }
+
+ $bg_x = round($bg_x + (float)$style->length_in_pt($style->border_left_width) * $dpi / 72);
+
+ if (Helpers::is_percent($bg_y)) {
+ // The point $bg_y % from the left edge of the image is placed
+ // $bg_y % from the left edge of the background rectangle
+ $p = ((float)$bg_y) / 100.0;
+ $y1 = $p * $img_h;
+ $y2 = $p * $bg_height;
+
+ $bg_y = $y2 - $y1;
+ } else {
+ $bg_y = (float)($style->length_in_pt($bg_y) * $dpi) / 72;
+ }
+
+ $bg_y = round($bg_y + (float)$style->length_in_pt($style->border_top_width) * $dpi / 72);
+
+ //clip background to the image area on partial repeat. Nothing to do if img off area
+ //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area
+ //On no repeat with positive offset: move size/start to have offset==0
+ //Handle x/y Dimensions separately
+
+ if ($repeat !== "repeat" && $repeat !== "repeat-x") {
+ //No repeat x
+ if ($bg_x < 0) {
+ $bg_width = $img_w + $bg_x;
+ } else {
+ $x += ($bg_x * 72) / $dpi;
+ $bg_width = $bg_width - $bg_x;
+ if ($bg_width > $img_w) {
+ $bg_width = $img_w;
+ }
+ $bg_x = 0;
+ }
+
+ if ($bg_width <= 0) {
+ return;
+ }
+
+ $width = (float)($bg_width * 72) / $dpi;
+ } else {
+ //repeat x
+ if ($bg_x < 0) {
+ $bg_x = -((-$bg_x) % $img_w);
+ } else {
+ $bg_x = $bg_x % $img_w;
+ if ($bg_x > 0) {
+ $bg_x -= $img_w;
+ }
+ }
+ }
+
+ if ($repeat !== "repeat" && $repeat !== "repeat-y") {
+ //no repeat y
+ if ($bg_y < 0) {
+ $bg_height = $img_h + $bg_y;
+ } else {
+ $y += ($bg_y * 72) / $dpi;
+ $bg_height = $bg_height - $bg_y;
+ if ($bg_height > $img_h) {
+ $bg_height = $img_h;
+ }
+ $bg_y = 0;
+ }
+ if ($bg_height <= 0) {
+ return;
+ }
+ $height = (float)($bg_height * 72) / $dpi;
+ } else {
+ //repeat y
+ if ($bg_y < 0) {
+ $bg_y = -((-$bg_y) % $img_h);
+ } else {
+ $bg_y = $bg_y % $img_h;
+ if ($bg_y > 0) {
+ $bg_y -= $img_h;
+ }
+ }
+ }
+
+ //Optimization, if repeat has no effect
+ if ($repeat === "repeat" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) {
+ $repeat = "repeat-x";
+ }
+
+ if ($repeat === "repeat" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) {
+ $repeat = "repeat-y";
+ }
+
+ if (($repeat === "repeat-x" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) ||
+ ($repeat === "repeat-y" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height)
+ ) {
+ $repeat = "no-repeat";
+ }
+
+ // Avoid rendering identical background-image variants multiple times
+ // This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color)
+ // Note: Here, bg_* are the start values, not end values after going through the tile loops!
+
+ $key = implode("_", [$bg_width, $bg_height, $img_w, $img_h, $bg_x, $bg_y, $repeat]);
+ // FIXME: This will fail when a file with that exact name exists in the
+ // same directory, included in the document as regular image
+ $cpdfKey = $img . "_" . $key;
+ $tmpFile = Cache::getTempImage($img, $key);
+ $cached = ($this->_canvas instanceof CPDF && $this->_canvas->get_cpdf()->image_iscached($cpdfKey))
+ || ($tmpFile !== null && file_exists($tmpFile));
+
+ if (!$cached) {
+ // img: image url string
+ // img_w, img_h: original image size in px
+ // width, height: box size in pt
+ // bg_width, bg_height: box size in px
+ // x, y: left/top edge of box on page in pt
+ // start_x, start_y: placement of image relative to pattern
+ // $repeat: repeat mode
+ // $bg: GD object of result image
+ // $src: GD object of original image
+
+ // Create a new image to fit over the background rectangle
+ $bg = imagecreatetruecolor($bg_width, $bg_height);
+ $cpdfFromGd = true;
+
+ switch (strtolower($type)) {
+ case "png":
+ $cpdfFromGd = false;
+ imagesavealpha($bg, true);
+ imagealphablending($bg, false);
+ $src = @imagecreatefrompng($img);
+ break;
+
+ case "jpeg":
+ $src = @imagecreatefromjpeg($img);
+ break;
+
+ case "webp":
+ $src = @imagecreatefromwebp($img);
+ break;
+
+ case "gif":
+ $src = @imagecreatefromgif($img);
+ break;
+
+ case "bmp":
+ $src = @Helpers::imagecreatefrombmp($img);
+ break;
+
+ default:
+ return; // Unsupported image type
+ }
+
+ if ($src == null) {
+ return;
+ }
+
+ if ($img_w != $org_img_w || $img_h != $org_img_h) {
+ $newSrc = imagescale($src, $img_w, $img_h);
+ imagedestroy($src);
+ $src = $newSrc;
+ }
+
+ if ($src == null) {
+ return;
+ }
+
+ //Background color if box is not relevant here
+ //Non transparent image: box clipped to real size. Background non relevant.
+ //Transparent image: The image controls the transparency and lets shine through whatever background.
+ //However on transparent image preset the composed image with the transparency color,
+ //to keep the transparency when copying over the non transparent parts of the tiles.
+ $ti = imagecolortransparent($src);
+ $palletsize = imagecolorstotal($src);
+
+ if ($ti >= 0 && $ti < $palletsize) {
+ $tc = imagecolorsforindex($src, $ti);
+ $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']);
+ imagefill($bg, 0, 0, $ti);
+ imagecolortransparent($bg, $ti);
+ }
+
+ //This has only an effect for the non repeatable dimension.
+ //compute start of src and dest coordinates of the single copy
+ if ($bg_x < 0) {
+ $dst_x = 0;
+ $src_x = -$bg_x;
+ } else {
+ $src_x = 0;
+ $dst_x = $bg_x;
+ }
+
+ if ($bg_y < 0) {
+ $dst_y = 0;
+ $src_y = -$bg_y;
+ } else {
+ $src_y = 0;
+ $dst_y = $bg_y;
+ }
+
+ //For historical reasons exchange meanings of variables:
+ //start_* will be the start values, while bg_* will be the temporary start values in the loops
+ $start_x = $bg_x;
+ $start_y = $bg_y;
+
+ // Copy regions from the source image to the background
+ if ($repeat === "no-repeat") {
+ // Simply place the image on the background
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h);
+
+ } elseif ($repeat === "repeat-x") {
+ for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) {
+ if ($bg_x < 0) {
+ $dst_x = 0;
+ $src_x = -$bg_x;
+ $w = $img_w + $bg_x;
+ } else {
+ $dst_x = $bg_x;
+ $src_x = 0;
+ $w = $img_w;
+ }
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h);
+ }
+ } elseif ($repeat === "repeat-y") {
+
+ for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) {
+ if ($bg_y < 0) {
+ $dst_y = 0;
+ $src_y = -$bg_y;
+ $h = $img_h + $bg_y;
+ } else {
+ $dst_y = $bg_y;
+ $src_y = 0;
+ $h = $img_h;
+ }
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h);
+ }
+ } elseif ($repeat === "repeat") {
+ for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) {
+ for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) {
+ if ($bg_x < 0) {
+ $dst_x = 0;
+ $src_x = -$bg_x;
+ $w = $img_w + $bg_x;
+ } else {
+ $dst_x = $bg_x;
+ $src_x = 0;
+ $w = $img_w;
+ }
+
+ if ($bg_y < 0) {
+ $dst_y = 0;
+ $src_y = -$bg_y;
+ $h = $img_h + $bg_y;
+ } else {
+ $dst_y = $bg_y;
+ $src_y = 0;
+ $h = $img_h;
+ }
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h);
+ }
+ }
+ } else {
+ print 'Unknown repeat!';
+ }
+
+ imagedestroy($src);
+
+ if ($cpdfFromGd && $this->_canvas instanceof CPDF) {
+ // Skip writing temp file as the GD object is added directly
+ } else {
+ $tmpDir = $this->_dompdf->getOptions()->getTempDir();
+ $tmpName = @tempnam($tmpDir, "bg_dompdf_img_");
+ @unlink($tmpName);
+ $tmpFile = "$tmpName.png";
+
+ imagepng($bg, $tmpFile);
+ imagedestroy($bg);
+
+ Cache::addTempImage($img, $tmpFile, $key);
+ }
+ } else {
+ $bg = null;
+ $cpdfFromGd = $tmpFile === null;
+ }
+
+ if ($this->_dompdf->getOptions()->getDebugPng()) {
+ print '[_background_image ' . $tmpFile . ']';
+ }
+
+ $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height);
+
+ // When using cpdf and optimization to direct png creation from gd object is available,
+ // don't create temp file, but place gd object directly into the pdf
+ if ($cpdfFromGd && $this->_canvas instanceof CPDF) {
+ // Note: CPDF_Adapter image converts y position
+ $this->_canvas->get_cpdf()->addImagePng($bg, $cpdfKey, $x, $this->_canvas->get_height() - $y - $height, $width, $height);
+
+ if (isset($bg)) {
+ imagedestroy($bg);
+ }
+ } else {
+ $this->_canvas->image($tmpFile, $x, $y, $width, $height);
+ }
+
+ $this->_canvas->clipping_end();
+ }
+
+ // Border rendering functions
+
+ /**
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
+ {
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2);
+ }
+
+ /**
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
+ {
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2);
+ }
+
+ /**
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
+ {
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2);
+ }
+
+ /**
+ * @param string $side
+ * @param float $ratio
+ * @param float $top
+ * @param float $right
+ * @param float $bottom
+ * @param float $left
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2)
+ {
+ switch ($side) {
+ case "top":
+ $r1 -= $left * $ratio;
+ $r2 -= $right * $ratio;
+ $x += $left * $ratio;
+ $y += $top * $ratio;
+ $length -= $left * $ratio + $right * $ratio;
+ break;
+
+ case "bottom":
+ $r1 -= $right * $ratio;
+ $r2 -= $left * $ratio;
+ $x += $left * $ratio;
+ $y -= $bottom * $ratio;
+ $length -= $left * $ratio + $right * $ratio;
+ break;
+
+ case "left":
+ $r1 -= $top * $ratio;
+ $r2 -= $bottom * $ratio;
+ $x += $left * $ratio;
+ $y += $top * $ratio;
+ $length -= $top * $ratio + $bottom * $ratio;
+ break;
+
+ case "right":
+ $r1 -= $bottom * $ratio;
+ $r2 -= $top * $ratio;
+ $x -= $right * $ratio;
+ $y += $top * $ratio;
+ $length -= $top * $ratio + $bottom * $ratio;
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ /**
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
+ {
+ list($top, $right, $bottom, $left) = $widths;
+
+ $third_widths = [$top / 3, $right / 3, $bottom / 3, $left / 3];
+
+ // draw the outer border
+ $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
+
+ $this->_apply_ratio($side, 2 / 3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
+
+ $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
+ }
+
+ /**
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
+ {
+ list($top, $right, $bottom, $left) = $widths;
+
+ $half_widths = [$top / 2, $right / 2, $bottom / 2, $left / 2];
+
+ $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
+
+ $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
+
+ $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
+ }
+
+ /**
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
+ {
+ list($top, $right, $bottom, $left) = $widths;
+
+ $half_widths = [$top / 2, $right / 2, $bottom / 2, $left / 2];
+
+ $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
+
+ $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
+
+ $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
+ }
+
+ /**
+ * @param $c
+ * @return mixed
+ */
+ protected function _tint($c)
+ {
+ if (!is_numeric($c)) {
+ return $c;
+ }
+
+ return min(1, $c + 0.16);
+ }
+
+ /**
+ * @param $c
+ * @return mixed
+ */
+ protected function _shade($c)
+ {
+ if (!is_numeric($c)) {
+ return $c;
+ }
+
+ return max(0, $c - 0.33);
+ }
+
+ /**
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
+ {
+ switch ($side) {
+ case "top":
+ case "left":
+ $shade = array_map([$this, "_shade"], $color);
+ $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
+ break;
+
+ case "bottom":
+ case "right":
+ $tint = array_map([$this, "_tint"], $color);
+ $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ /**
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0)
+ {
+ switch ($side) {
+ case "top":
+ case "left":
+ $tint = array_map([$this, "_tint"], $color);
+ $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
+ break;
+
+ case "bottom":
+ case "right":
+ $shade = array_map([$this, "_shade"], $color);
+ $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ /**
+ * Get the dash pattern and cap style for the given border style, width, and
+ * line length.
+ *
+ * The base pattern is adjusted so that it fits the given line length
+ * symmetrically.
+ *
+ * @param string $style
+ * @param float $width
+ * @param float $length
+ *
+ * @return array
+ */
+ protected function dashPattern(string $style, float $width, float $length): array
+ {
+ if ($style === "dashed") {
+ $w = 3 * $width;
+
+ if ($length < $w) {
+ $s = $w;
+ } else {
+ // Scale dashes and gaps
+ $r = round($length / $w);
+ $r = $r % 2 === 0 ? $r + 1 : $r;
+ $s = $length / $r;
+ }
+
+ return [[$s], "butt"];
+ }
+
+ if ($style === "dotted") {
+ // Draw circles along the line
+ // Round caps extend outwards by half line width, so a zero dash
+ // width results in a circle
+ $gap = $width <= 1 ? 2 : 1;
+ $w = ($gap + 1) * $width;
+
+ if ($length < $w) {
+ $s = $w;
+ } else {
+ // Only scale gaps
+ $l = $length - $width;
+ $r = max(round($l / $w), 1);
+ $s = $l / $r;
+ }
+
+ return [[0, $s], "round"];
+ }
+
+ return [[], "butt"];
+ }
+
+ /**
+ * Draws a solid, dotted, or dashed line, observing the border radius
+ *
+ * @param float $x
+ * @param float $y
+ * @param float $length
+ * @param array $color
+ * @param float[] $widths
+ * @param string $side
+ * @param string $corner_style
+ * @param string $pattern_name
+ * @param float $r1
+ * @param float $r2
+ */
+ protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name = "none", $r1 = 0, $r2 = 0)
+ {
+ /** used by $$side */
+ [$top, $right, $bottom, $left] = $widths;
+ $width = $$side;
+
+ // No need to clip corners if border radius is large enough
+ $cornerClip = $corner_style === "bevel" && ($r1 < $width || $r2 < $width);
+ $lineLength = $length - $r1 - $r2;
+ [$pattern, $cap] = $this->dashPattern($pattern_name, $width, $lineLength);
+
+ // Determine arc border radius for corner arcs
+ $halfWidth = $width / 2;
+ $ar1 = max($r1 - $halfWidth, 0);
+ $ar2 = max($r2 - $halfWidth, 0);
+
+ // Small angle adjustments to prevent the background from shining through
+ $adj1 = $ar1 / 80;
+ $adj2 = $ar2 / 80;
+
+ // Adjust line width and corner angles to account for the fact that
+ // round caps extend outwards. The line is actually only shifted below,
+ // not shortened, as otherwise the end dash (circle) will vanish
+ // occasionally
+ $dl = $cap === "round" ? $halfWidth : 0;
+
+ if ($cap === "round" && $ar1 > 0) {
+ $adj1 -= rad2deg(asin($halfWidth / $ar1));
+ }
+ if ($cap === "round" && $ar2 > 0) {
+ $adj2 -= rad2deg(asin($halfWidth / $ar2));
+ }
+
+ switch ($side) {
+ case "top":
+ if ($cornerClip) {
+ $points = [
+ $x, $y,
+ $x, $y - 1, // Extend outwards to avoid gaps
+ $x + $length, $y - 1, // Extend outwards to avoid gaps
+ $x + $length, $y,
+ $x + $length - max($right, $r2), $y + max($width, $r2),
+ $x + max($left, $r1), $y + max($width, $r1)
+ ];
+ $this->_canvas->clipping_polygon($points);
+ }
+
+ $y += $halfWidth;
+
+ if ($ar1 > 0 && $adj1 > -22.5) {
+ $this->_canvas->arc($x + $r1, $y + $ar1, $ar1, $ar1, 90 - $adj1, 135 + $adj1, $color, $width, $pattern, $cap);
+ }
+
+ if ($lineLength > 0) {
+ $this->_canvas->line($x + $dl + $r1, $y, $x + $dl + $length - $r2, $y, $color, $width, $pattern, $cap);
+ }
+
+ if ($ar2 > 0 && $adj2 > -22.5) {
+ $this->_canvas->arc($x + $length - $r2, $y + $ar2, $ar2, $ar2, 45 - $adj2, 90 + $adj2, $color, $width, $pattern, $cap);
+ }
+ break;
+
+ case "bottom":
+ if ($cornerClip) {
+ $points = [
+ $x, $y,
+ $x, $y + 1, // Extend outwards to avoid gaps
+ $x + $length, $y + 1, // Extend outwards to avoid gaps
+ $x + $length, $y,
+ $x + $length - max($right, $r2), $y - max($width, $r2),
+ $x + max($left, $r1), $y - max($width, $r1)
+ ];
+ $this->_canvas->clipping_polygon($points);
+ }
+
+ $y -= $halfWidth;
+
+ if ($ar1 > 0 && $adj1 > -22.5) {
+ $this->_canvas->arc($x + $r1, $y - $ar1, $ar1, $ar1, 225 - $adj1, 270 + $adj1, $color, $width, $pattern, $cap);
+ }
+
+ if ($lineLength > 0) {
+ $this->_canvas->line($x + $dl + $r1, $y, $x + $dl + $length - $r2, $y, $color, $width, $pattern, $cap);
+ }
+
+ if ($ar2 > 0 && $adj2 > -22.5) {
+ $this->_canvas->arc($x + $length - $r2, $y - $ar2, $ar2, $ar2, 270 - $adj2, 315 + $adj2, $color, $width, $pattern, $cap);
+ }
+ break;
+
+ case "left":
+ if ($cornerClip) {
+ $points = [
+ $x, $y,
+ $x - 1, $y, // Extend outwards to avoid gaps
+ $x - 1, $y + $length, // Extend outwards to avoid gaps
+ $x, $y + $length,
+ $x + max($width, $r2), $y + $length - max($bottom, $r2),
+ $x + max($width, $r1), $y + max($top, $r1)
+ ];
+ $this->_canvas->clipping_polygon($points);
+ }
+
+ $x += $halfWidth;
+
+ if ($ar1 > 0 && $adj1 > -22.5) {
+ $this->_canvas->arc($x + $ar1, $y + $r1, $ar1, $ar1, 135 - $adj1, 180 + $adj1, $color, $width, $pattern, $cap);
+ }
+
+ if ($lineLength > 0) {
+ $this->_canvas->line($x, $y + $dl + $r1, $x, $y + $dl + $length - $r2, $color, $width, $pattern, $cap);
+ }
+
+ if ($ar2 > 0 && $adj2 > -22.5) {
+ $this->_canvas->arc($x + $ar2, $y + $length - $r2, $ar2, $ar2, 180 - $adj2, 225 + $adj2, $color, $width, $pattern, $cap);
+ }
+ break;
+
+ case "right":
+ if ($cornerClip) {
+ $points = [
+ $x, $y,
+ $x + 1, $y, // Extend outwards to avoid gaps
+ $x + 1, $y + $length, // Extend outwards to avoid gaps
+ $x, $y + $length,
+ $x - max($width, $r2), $y + $length - max($bottom, $r2),
+ $x - max($width, $r1), $y + max($top, $r1)
+ ];
+ $this->_canvas->clipping_polygon($points);
+ }
+
+ $x -= $halfWidth;
+
+ if ($ar1 > 0 && $adj1 > -22.5) {
+ $this->_canvas->arc($x - $ar1, $y + $r1, $ar1, $ar1, 0 - $adj1, 45 + $adj1, $color, $width, $pattern, $cap);
+ }
+
+ if ($lineLength > 0) {
+ $this->_canvas->line($x, $y + $dl + $r1, $x, $y + $dl + $length - $r2, $color, $width, $pattern, $cap);
+ }
+
+ if ($ar2 > 0 && $adj2 > -22.5) {
+ $this->_canvas->arc($x - $ar2, $y + $length - $r2, $ar2, $ar2, 315 - $adj2, 360 + $adj2, $color, $width, $pattern, $cap);
+ }
+ break;
+ }
+
+ if ($cornerClip) {
+ $this->_canvas->clipping_end();
+ }
+ }
+
+ /**
+ * @param float $opacity
+ */
+ protected function _set_opacity(float $opacity): void
+ {
+ if ($opacity >= 0.0 && $opacity <= 1.0) {
+ $this->_canvas->set_opacity($opacity);
+ }
+ }
+
+ /**
+ * @param float[] $box
+ * @param string $color
+ * @param array $style
+ */
+ protected function _debug_layout($box, $color = "red", $style = [])
+ {
+ $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], Color::parse($color), 0.1, $style);
+ }
+
+ /**
+ * @param float $img_width
+ * @param float $img_height
+ * @param float $container_width
+ * @param float $container_height
+ * @param array|string $bg_resize
+ * @param int $dpi
+ *
+ * @return array
+ */
+ protected function _resize_background_image(
+ $img_width,
+ $img_height,
+ $container_width,
+ $container_height,
+ $bg_resize,
+ $dpi
+ ) {
+ // We got two some specific numbers and/or auto definitions
+ if (is_array($bg_resize)) {
+ $is_auto_width = $bg_resize[0] === 'auto';
+ if ($is_auto_width) {
+ $new_img_width = $img_width;
+ } else {
+ $new_img_width = $bg_resize[0];
+ if (Helpers::is_percent($new_img_width)) {
+ $new_img_width = round(($container_width / 100) * (float)$new_img_width);
+ } else {
+ $new_img_width = round($new_img_width * $dpi / 72);
+ }
+ }
+
+ $is_auto_height = $bg_resize[1] === 'auto';
+ if ($is_auto_height) {
+ $new_img_height = $img_height;
+ } else {
+ $new_img_height = $bg_resize[1];
+ if (Helpers::is_percent($new_img_height)) {
+ $new_img_height = round(($container_height / 100) * (float)$new_img_height);
+ } else {
+ $new_img_height = round($new_img_height * $dpi / 72);
+ }
+ }
+
+ // if one of both was set to auto the other one needs to scale proportionally
+ if ($is_auto_width !== $is_auto_height) {
+ if ($is_auto_height) {
+ $new_img_height = round($new_img_width * ($img_height / $img_width));
+ } else {
+ $new_img_width = round($new_img_height * ($img_width / $img_height));
+ }
+ }
+ } else {
+ $container_ratio = $container_height / $container_width;
+
+ if ($bg_resize === 'cover' || $bg_resize === 'contain') {
+ $img_ratio = $img_height / $img_width;
+
+ if (
+ ($bg_resize === 'cover' && $container_ratio > $img_ratio) ||
+ ($bg_resize === 'contain' && $container_ratio < $img_ratio)
+ ) {
+ $new_img_height = $container_height;
+ $new_img_width = round($container_height / $img_ratio);
+ } else {
+ $new_img_width = $container_width;
+ $new_img_height = round($container_width * $img_ratio);
+ }
+ } else {
+ $new_img_width = $img_width;
+ $new_img_height = $img_height;
+ }
+ }
+
+ return [$new_img_width, $new_img_height];
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Block.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Block.php
new file mode 100644
index 000000000..99db1929a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Block.php
@@ -0,0 +1,88 @@
+get_style();
+ $node = $frame->get_node();
+ $dompdf = $this->_dompdf;
+
+ $this->_set_opacity($frame->get_opacity($style->opacity));
+
+ [$x, $y, $w, $h] = $frame->get_border_box();
+
+ if ($node->nodeName === "body") {
+ // Margins should be fully resolved at this point
+ $mt = $style->margin_top;
+ $mb = $style->margin_bottom;
+ $h = $frame->get_containing_block("h") - $mt - $mb;
+ }
+
+ $border_box = [$x, $y, $w, $h];
+
+ // Draw our background, border and content
+ $this->_render_background($frame, $border_box);
+ $this->_render_border($frame, $border_box);
+ $this->_render_outline($frame, $border_box);
+
+ // Handle anchors & links
+ if ($node->nodeName === "a" && $href = $node->getAttribute("href")) {
+ $href = Helpers::build_url($dompdf->getProtocol(), $dompdf->getBaseHost(), $dompdf->getBasePath(), $href) ?? $href;
+ $this->_canvas->add_link($href, $x, $y, $w, $h);
+ }
+
+ $id = $frame->get_node()->getAttribute("id");
+ if (strlen($id) > 0) {
+ $this->_canvas->add_named_dest($id);
+ }
+
+ $this->debugBlockLayout($frame, "red", false);
+ }
+
+ protected function debugBlockLayout(Frame $frame, ?string $color, bool $lines = false): void
+ {
+ $options = $this->_dompdf->getOptions();
+ $debugLayout = $options->getDebugLayout();
+
+ if (!$debugLayout) {
+ return;
+ }
+
+ if ($color && $options->getDebugLayoutBlocks()) {
+ $this->_debug_layout($frame->get_border_box(), $color);
+
+ if ($options->getDebugLayoutPaddingBox()) {
+ $this->_debug_layout($frame->get_padding_box(), $color, [0.5, 0.5]);
+ }
+ }
+
+ if ($lines && $options->getDebugLayoutLines() && $frame instanceof BlockFrameDecorator) {
+ [$cx, , $cw] = $frame->get_content_box();
+
+ foreach ($frame->get_line_boxes() as $line) {
+ $lw = $cw - $line->left - $line->right;
+ $this->_debug_layout([$cx + $line->left, $line->y, $lw, $line->h], "orange");
+ }
+ }
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Image.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Image.php
new file mode 100644
index 000000000..61f684f94
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Image.php
@@ -0,0 +1,90 @@
+get_style();
+ $border_box = $frame->get_border_box();
+
+ $this->_set_opacity($frame->get_opacity($style->opacity));
+
+ // Render background & borders
+ $this->_render_background($frame, $border_box);
+ $this->_render_border($frame, $border_box);
+ $this->_render_outline($frame, $border_box);
+
+ $content_box = $frame->get_content_box();
+ [$x, $y, $w, $h] = $content_box;
+
+ $src = $frame->get_image_url();
+ $alt = null;
+
+ if (Cache::is_broken($src) &&
+ $alt = $frame->get_node()->getAttribute("alt")
+ ) {
+ $font = $style->font_family;
+ $size = $style->font_size;
+ $word_spacing = $style->word_spacing;
+ $letter_spacing = $style->letter_spacing;
+
+ $this->_canvas->text(
+ $x,
+ $y,
+ $alt,
+ $font,
+ $size,
+ $style->color,
+ $word_spacing,
+ $letter_spacing
+ );
+ } elseif ($w > 0 && $h > 0) {
+ if ($style->has_border_radius()) {
+ [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box, $content_box);
+ $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
+ }
+
+ $this->_canvas->image($src, $x, $y, $w, $h, $style->image_resolution);
+
+ if ($style->has_border_radius()) {
+ $this->_canvas->clipping_end();
+ }
+ }
+
+ if ($msg = $frame->get_image_msg()) {
+ $parts = preg_split("/\s*\n\s*/", $msg);
+ $font = $style->font_family;
+ $height = 10;
+ $_y = $alt ? $y + $h - count($parts) * $height : $y;
+
+ foreach ($parts as $i => $_part) {
+ $this->_canvas->text($x, $_y + $i * $height, $_part, $font, $height * 0.8, [0.5, 0.5, 0.5]);
+ }
+ }
+
+ $id = $frame->get_node()->getAttribute("id");
+ if (strlen($id) > 0) {
+ $this->_canvas->add_named_dest($id);
+ }
+
+ $this->debugBlockLayout($frame, "blue");
+ }
+}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Inline.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Inline.php
new file mode 100644
index 000000000..ad3546492
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Inline.php
@@ -0,0 +1,126 @@
+get_first_child()) {
+ return; // No children, no service
+ }
+
+ $style = $frame->get_style();
+ $dompdf = $this->_dompdf;
+
+ $this->_set_opacity($frame->get_opacity($style->opacity));
+
+ $do_debug_layout_line = $dompdf->getOptions()->getDebugLayout()
+ && $dompdf->getOptions()->getDebugLayoutInline();
+
+ // Draw the background & border behind each child. To do this we need
+ // to figure out just how much space each child takes:
+ [$x, $y] = $frame->get_first_child()->get_position();
+ [$w, $h] = $this->get_child_size($frame, $do_debug_layout_line);
+
+ [, , $cbw] = $frame->get_containing_block();
+ $margin_left = $style->length_in_pt($style->margin_left, $cbw);
+ $pt = $style->length_in_pt($style->padding_top, $cbw);
+ $pb = $style->length_in_pt($style->padding_bottom, $cbw);
+
+ // Make sure that border and background start inside the left margin
+ // Extend the drawn box by border and padding in vertical direction, as
+ // these do not affect layout
+ // FIXME: Using a small vertical offset of a fraction of the height here
+ // to work around the vertical position being slightly off in general
+ $x += $margin_left;
+ $y -= $style->border_top_width + $pt - ($h * 0.1);
+ $w += $style->border_left_width + $style->border_right_width;
+ $h += $style->border_top_width + $pt + $style->border_bottom_width + $pb;
+
+ $border_box = [$x, $y, $w, $h];
+ $this->_render_background($frame, $border_box);
+ $this->_render_border($frame, $border_box);
+ $this->_render_outline($frame, $border_box);
+
+ $node = $frame->get_node();
+ $id = $node->getAttribute("id");
+ if (strlen($id) > 0) {
+ $this->_canvas->add_named_dest($id);
+ }
+
+ // Only two levels of links frames
+ $is_link_node = $node->nodeName === "a";
+ if ($is_link_node) {
+ if (($name = $node->getAttribute("name"))) {
+ $this->_canvas->add_named_dest($name);
+ }
+ }
+
+ if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") {
+ $link_node = $frame->get_parent()->get_node();
+ }
+
+ // Handle anchors & links
+ if ($is_link_node) {
+ if ($href = $node->getAttribute("href")) {
+ $href = Helpers::build_url($dompdf->getProtocol(), $dompdf->getBaseHost(), $dompdf->getBasePath(), $href) ?? $href;
+ $this->_canvas->add_link($href, $x, $y, $w, $h);
+ }
+ }
+ }
+
+ protected function get_child_size(Frame $frame, bool $do_debug_layout_line): array
+ {
+ $w = 0.0;
+ $h = 0.0;
+
+ foreach ($frame->get_children() as $child) {
+ if ($child->get_node()->nodeValue === " " && $child->get_prev_sibling() && !$child->get_next_sibling()) {
+ break;
+ }
+
+ $style = $child->get_style();
+ $auto_width = $style->width === "auto";
+ $auto_height = $style->height === "auto";
+ [, , $child_w, $child_h] = $child->get_padding_box();
+
+ if ($auto_width || $auto_height) {
+ [$child_w2, $child_h2] = $this->get_child_size($child, $do_debug_layout_line);
+
+ if ($auto_width) {
+ $child_w = $child_w2;
+ }
+
+ if ($auto_height) {
+ $child_h = $child_h2;
+ }
+ }
+
+ $w += $child_w;
+ $h = max($h, $child_h);
+
+ if ($do_debug_layout_line) {
+ $this->_debug_layout($child->get_border_box(), "blue");
+
+ if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) {
+ $this->_debug_layout($child->get_padding_box(), "blue", [0.5, 0.5]);
+ }
+ }
+ }
+
+ return [$w, $h];
+ }
+}
diff --git a/library/vendor/dompdf/src/Renderer/ListBullet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/ListBullet.php
similarity index 63%
rename from library/vendor/dompdf/src/Renderer/ListBullet.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/ListBullet.php
index fa0bc3731..2df6696ad 100644
--- a/library/vendor/dompdf/src/Renderer/ListBullet.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/ListBullet.php
@@ -1,22 +1,20 @@
- * @author Helmut Tischer
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\Renderer;
use Dompdf\Helpers;
use Dompdf\Frame;
-use Dompdf\Image\Cache;
use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator;
+use Dompdf\FrameDecorator\ListBulletImage;
+use Dompdf\Image\Cache;
/**
* Renders list bullets
*
- * @access private
* @package dompdf
*/
class ListBullet extends AbstractRenderer
@@ -27,7 +25,7 @@ class ListBullet extends AbstractRenderer
*/
static function get_counter_chars($type)
{
- static $cache = array();
+ static $cache = [];
if (isset($cache[$type])) {
return $cache[$type];
@@ -75,9 +73,9 @@ class ListBullet extends AbstractRenderer
}
/**
- * @param integer $n
+ * @param int $n
* @param string $type
- * @param integer $pad
+ * @param int|null $pad
*
* @return string
*/
@@ -105,7 +103,7 @@ class ListBullet extends AbstractRenderer
case "lower-alpha":
case "lower-latin":
case "a":
- $text = chr(($n % 26) + ord('a') - 1);
+ $text = chr((($n - 1) % 26) + ord('a'));
break;
case "upper-roman":
@@ -129,68 +127,54 @@ class ListBullet extends AbstractRenderer
}
/**
- * @param Frame $frame
+ * @param ListBulletFrameDecorator $frame
*/
function render(Frame $frame)
{
+ $li = $frame->get_parent();
$style = $frame->get_style();
- $font_size = $style->font_size;
- $line_height = (float)$style->length_in_pt($style->line_height, $frame->get_containing_block("h"));
$this->_set_opacity($frame->get_opacity($style->opacity));
- $li = $frame->get_parent();
-
- // Don't render bullets twice if if was split
- if ($li->_splitted) {
+ // Don't render bullets twice if the list item was split
+ if ($li->is_split_off) {
return;
}
+ $font_family = $style->font_family;
+ $font_size = $style->font_size;
+ $baseline = $this->_canvas->get_font_baseline($font_family, $font_size);
+
// Handle list-style-image
// If list style image is requested but missing, fall back to predefined types
- if ($style->list_style_image !== "none" && !Cache::is_broken($img = $frame->get_image_url())) {
- list($x, $y) = $frame->get_position();
-
- //For expected size and aspect, instead of box size, use image natural size scaled to DPI.
- // Resample the bullet image to be consistent with 'auto' sized images
- // See also Image::get_min_max_width
- // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
- //$w = $frame->get_width();
- //$h = $frame->get_height();
- list($width, $height) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext());
- $dpi = $this->_dompdf->getOptions()->getDpi();
- $w = ((float)rtrim($width, "px") * 72) / $dpi;
- $h = ((float)rtrim($height, "px") * 72) / $dpi;
-
- $x -= $w;
- $y -= ($line_height - $font_size) / 2; //Reverse hinting of list_bullet_positioner
+ if ($frame instanceof ListBulletImage && !Cache::is_broken($img = $frame->get_image_url())) {
+ [$x, $y] = $frame->get_position();
+ $w = $frame->get_width();
+ $h = $frame->get_height();
+ $y += $baseline - $h;
$this->_canvas->image($img, $x, $y, $w, $h);
} else {
$bullet_style = $style->list_style_type;
- $fill = false;
-
switch ($bullet_style) {
default:
- /** @noinspection PhpMissingBreakStatementInspection */
case "disc":
- $fill = true;
-
case "circle":
- list($x, $y) = $frame->get_position();
- $r = ($font_size * (ListBulletFrameDecorator::BULLET_SIZE /*-ListBulletFrameDecorator::BULLET_THICKNESS*/)) / 2;
- $x -= $font_size * (ListBulletFrameDecorator::BULLET_SIZE / 2);
- $y += ($font_size * (1 - ListBulletFrameDecorator::BULLET_DESCENT)) / 2;
+ [$x, $y] = $frame->get_position();
+ $offset = $font_size * ListBulletFrameDecorator::BULLET_OFFSET;
+ $r = ($font_size * ListBulletFrameDecorator::BULLET_SIZE) / 2;
+ $x += $r;
+ $y += $baseline - $r - $offset;
$o = $font_size * ListBulletFrameDecorator::BULLET_THICKNESS;
- $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill);
+ $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $bullet_style !== "circle");
break;
case "square":
- list($x, $y) = $frame->get_position();
+ [$x, $y] = $frame->get_position();
+ $offset = $font_size * ListBulletFrameDecorator::BULLET_OFFSET;
$w = $font_size * ListBulletFrameDecorator::BULLET_SIZE;
- $x -= $w;
- $y += ($font_size * (1 - ListBulletFrameDecorator::BULLET_DESCENT - ListBulletFrameDecorator::BULLET_SIZE)) / 2;
+ $y += $baseline - $w - $offset;
$this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color);
break;
@@ -222,25 +206,21 @@ class ListBullet extends AbstractRenderer
$index = $node->getAttribute("dompdf-counter");
$text = $this->make_counter($index, $bullet_style, $pad);
- if (trim($text) == "") {
+ if (trim($text) === "") {
return;
}
- $spacing = 0;
- $font_family = $style->font_family;
+ $word_spacing = $style->word_spacing;
+ $letter_spacing = $style->letter_spacing;
+ $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font_family, $font_size, $word_spacing, $letter_spacing);
- $line = $li->get_containing_line();
- list($x, $y) = array($frame->get_position("x"), $line->y);
-
- $x -= $this->_dompdf->getFontMetrics()->getTextWidth($text, $font_family, $font_size, $spacing);
-
- // Take line-height into account
- $line_height = $style->line_height;
- $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results
+ [$x, $y] = $frame->get_position();
+ // Correct for static frame width applied by positioner
+ $x += $frame->get_width() - $text_width;
$this->_canvas->text($x, $y, $text,
$font_family, $font_size,
- $style->color, $spacing);
+ $style->color, $word_spacing, $letter_spacing);
case "none":
break;
@@ -248,7 +228,7 @@ class ListBullet extends AbstractRenderer
}
$id = $frame->get_node()->getAttribute("id");
- if (strlen($id) > 0) {
+ if (strlen($id) > 0) {
$this->_canvas->add_named_dest($id);
}
}
diff --git a/library/vendor/dompdf/src/Renderer/TableCell.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableCell.php
similarity index 66%
rename from library/vendor/dompdf/src/Renderer/TableCell.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableCell.php
index 8e607927c..cbbffd34c 100644
--- a/library/vendor/dompdf/src/Renderer/TableCell.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableCell.php
@@ -1,8 +1,7 @@
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\Renderer;
@@ -30,38 +29,56 @@ class TableCell extends Block
}
$this->_set_opacity($frame->get_opacity($style->opacity));
- list($x, $y, $w, $h) = $frame->get_border_box();
-
- // Draw our background, border and content
- if (($bg = $style->background_color) !== "transparent") {
- $this->_canvas->filled_rectangle($x, $y, (float)$w, (float)$h, $bg);
- }
-
- if (($url = $style->background_image) && $url !== "none") {
- $this->_background_image($url, $x, $y, $w, $h, $style);
- }
+ $border_box = $frame->get_border_box();
$table = Table::find_parent_table($frame);
if ($table->get_style()->border_collapse !== "collapse") {
- $this->_render_border($frame);
- $this->_render_outline($frame);
- return;
+ $this->_render_background($frame, $border_box);
+ $this->_render_border($frame, $border_box);
+ $this->_render_outline($frame, $border_box);
+ } else {
+ // The collapsed case is slightly complicated...
+
+ $cells = $table->get_cellmap()->get_spanned_cells($frame);
+
+ if (is_null($cells)) {
+ return;
+ }
+
+ // Render the background to the padding box, as the cells are
+ // rendered individually one after another, and we don't want the
+ // background to overlap an adjacent border
+ $padding_box = $frame->get_padding_box();
+
+ $this->_render_background($frame, $padding_box);
+ $this->_render_collapsed_border($frame, $table);
+
+ // FIXME: Outline should be drawn over other cells
+ $this->_render_outline($frame, $border_box);
}
- // The collapsed case is slightly complicated...
- // @todo Add support for outlines here
+ $id = $frame->get_node()->getAttribute("id");
+ if (strlen($id) > 0) {
+ $this->_canvas->add_named_dest($id);
+ }
+ // $this->debugBlockLayout($frame, "red", false);
+ }
+
+ /**
+ * @param Frame $frame
+ * @param Table $table
+ */
+ protected function _render_collapsed_border(Frame $frame, Table $table): void
+ {
$cellmap = $table->get_cellmap();
$cells = $cellmap->get_spanned_cells($frame);
-
- if (is_null($cells)) {
- return;
- }
-
$num_rows = $cellmap->get_num_rows();
$num_cols = $cellmap->get_num_cols();
+ [$table_x, $table_y] = $table->get_position();
+
// Determine the top row spanned by this cell
$i = $cells["rows"][0];
$top_row = $cellmap->get_row($i);
@@ -79,46 +96,45 @@ class TableCell extends Block
// Draw the horizontal borders
foreach ($cells["columns"] as $j) {
$bp = $cellmap->get_border_properties($i, $j);
-
- $y = $top_row["y"] - $bp["top"]["width"] / 2;
-
$col = $cellmap->get_column($j);
- $x = $col["x"] - $bp["left"]["width"] / 2;
+
+ $x = $table_x + $col["x"] - $bp["left"]["width"] / 2;
+ $y = $table_y + $top_row["y"] - $bp["top"]["width"] / 2;
$w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"]) / 2;
- if ($bp["top"]["style"] !== "none" && $bp["top"]["width"] > 0) {
- $widths = array(
+ if ($bp["top"]["width"] > 0) {
+ $widths = [
(float)$bp["top"]["width"],
(float)$bp["right"]["width"],
(float)$bp["bottom"]["width"],
(float)$bp["left"]["width"]
- );
+ ];
+
$method = "_border_" . $bp["top"]["style"];
$this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top", "square");
}
if ($draw_bottom) {
$bp = $cellmap->get_border_properties($num_rows - 1, $j);
- if ($bp["bottom"]["style"] === "none" || $bp["bottom"]["width"] <= 0) {
+ if ($bp["bottom"]["width"] <= 0) {
continue;
}
-
- $y = $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2;
-
- $widths = array(
+
+ $widths = [
(float)$bp["top"]["width"],
(float)$bp["right"]["width"],
(float)$bp["bottom"]["width"],
(float)$bp["left"]["width"]
- );
+ ];
+
+ $y = $table_y + $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2;
+
$method = "_border_" . $bp["bottom"]["style"];
$this->$method($x, $y, $w, $bp["bottom"]["color"], $widths, "bottom", "square");
-
}
}
$j = $cells["columns"][0];
-
$left_col = $cellmap->get_column($j);
if (in_array($num_cols - 1, $cells["columns"])) {
@@ -131,21 +147,19 @@ class TableCell extends Block
// Draw the vertical borders
foreach ($cells["rows"] as $i) {
$bp = $cellmap->get_border_properties($i, $j);
-
- $x = $left_col["x"] - $bp["left"]["width"] / 2;
-
$row = $cellmap->get_row($i);
- $y = $row["y"] - $bp["top"]["width"] / 2;
+ $x = $table_x + $left_col["x"] - $bp["left"]["width"] / 2;
+ $y = $table_y + $row["y"] - $bp["top"]["width"] / 2;
$h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"]) / 2;
- if ($bp["left"]["style"] !== "none" && $bp["left"]["width"] > 0) {
- $widths = array(
+ if ($bp["left"]["width"] > 0) {
+ $widths = [
(float)$bp["top"]["width"],
(float)$bp["right"]["width"],
(float)$bp["bottom"]["width"],
(float)$bp["left"]["width"]
- );
+ ];
$method = "_border_" . $bp["left"]["style"];
$this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left", "square");
@@ -153,27 +167,22 @@ class TableCell extends Block
if ($draw_right) {
$bp = $cellmap->get_border_properties($i, $num_cols - 1);
- if ($bp["right"]["style"] === "none" || $bp["right"]["width"] <= 0) {
+ if ($bp["right"]["width"] <= 0) {
continue;
}
- $x = $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2;
-
- $widths = array(
+ $widths = [
(float)$bp["top"]["width"],
(float)$bp["right"]["width"],
(float)$bp["bottom"]["width"],
(float)$bp["left"]["width"]
- );
+ ];
+
+ $x = $table_x + $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2;
$method = "_border_" . $bp["right"]["style"];
$this->$method($x, $y, $h, $bp["right"]["color"], $widths, "right", "square");
}
}
-
- $id = $frame->get_node()->getAttribute("id");
- if (strlen($id) > 0) {
- $this->_canvas->add_named_dest($id);
- }
}
}
diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableRowGroup.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableRowGroup.php
new file mode 100644
index 000000000..295ccde3e
--- /dev/null
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableRowGroup.php
@@ -0,0 +1,40 @@
+get_style();
+
+ $this->_set_opacity($frame->get_opacity($style->opacity));
+
+ $border_box = $frame->get_border_box();
+
+ $this->_render_border($frame, $border_box);
+ $this->_render_outline($frame, $border_box);
+
+ $id = $frame->get_node()->getAttribute("id");
+ if (strlen($id) > 0) {
+ $this->_canvas->add_named_dest($id);
+ }
+
+ $this->debugBlockLayout($frame, "red");
+ }
+}
diff --git a/library/vendor/dompdf/src/Renderer/Text.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Text.php
similarity index 80%
rename from library/vendor/dompdf/src/Renderer/Text.php
rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Text.php
index 51e6e6259..e7baa0a01 100644
--- a/library/vendor/dompdf/src/Renderer/Text.php
+++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Text.php
@@ -1,10 +1,7 @@
- * @author Helmut Tischer
- * @author Fabien Ménager
+ * @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\Renderer;
@@ -45,35 +42,29 @@ class Text extends AbstractRenderer
*/
function render(Frame $frame)
{
+ $style = $frame->get_style();
$text = $frame->get_text();
+
if (trim($text) === "") {
return;
}
- $style = $frame->get_style();
+ $this->_set_opacity($frame->get_opacity($style->opacity));
+
list($x, $y) = $frame->get_position();
$cb = $frame->get_containing_block();
- if (($ml = $style->margin_left) === "auto" || $ml === "none") {
- $ml = 0;
- }
-
- if (($pl = $style->padding_left) === "auto" || $pl === "none") {
- $pl = 0;
- }
-
- if (($bl = $style->border_left_width) === "auto" || $bl === "none") {
- $bl = 0;
- }
-
- $x += (float)$style->length_in_pt(array($ml, $pl, $bl), $cb["w"]);
+ $ml = $style->margin_left;
+ $pl = $style->padding_left;
+ $bl = $style->border_left_width;
+ $x += (float) $style->length_in_pt([$ml, $pl, $bl], $cb["w"]);
$font = $style->font_family;
$size = $style->font_size;
$frame_font_size = $frame->get_dompdf()->getFontMetrics()->getFontHeight($font, $size);
- $word_spacing = $frame->get_text_spacing() + (float)$style->length_in_pt($style->word_spacing);
- $char_spacing = (float)$style->length_in_pt($style->letter_spacing);
- $width = $style->width;
+ $word_spacing = $frame->get_text_spacing() + $style->word_spacing;
+ $letter_spacing = $style->letter_spacing;
+ $width = (float) $style->width;
/*$text = str_replace(
array("{PAGE_NUM}"),
@@ -83,7 +74,7 @@ class Text extends AbstractRenderer
$this->_canvas->text($x, $y, $text,
$font, $size,
- $style->color, $word_spacing, $char_spacing);
+ $style->color, $word_spacing, $letter_spacing);
$line = $frame->get_containing_line();
@@ -121,7 +112,7 @@ class Text extends AbstractRenderer
// Draw all applicable text-decorations. Start with the root and work our way down.
$p = $frame;
- $stack = array();
+ $stack = [];
while ($p = $p->get_parent()) {
$stack[] = $p;
}
@@ -160,8 +151,8 @@ class Text extends AbstractRenderer
}
if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines()) {
- $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size);
- $this->_debug_layout(array($x, $y, $text_width + ($line->wc - 1) * $word_spacing, $frame_font_size), "orange", array(0.5, 0.5));
+ $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $letter_spacing);
+ $this->_debug_layout([$x, $y, $text_width, $frame_font_size], "orange", [0.5, 0.5]);
}
}
}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/CREDITS b/library/vendor/dompdf/vendor/masterminds/html5/CREDITS
new file mode 100644
index 000000000..c2dbc4b64
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/CREDITS
@@ -0,0 +1,11 @@
+Matt Butcher [technosophos] (lead)
+Matt Farina [mattfarina] (lead)
+Asmir Mustafic [goetas] (contributor)
+Edward Z. Yang [ezyang] (contributor)
+Geoffrey Sneddon [gsnedders] (contributor)
+Kukhar Vasily [ngreduce] (contributor)
+Rune Christensen [MrElectronic] (contributor)
+Mišo Belica [miso-belica] (contributor)
+Asmir Mustafic [goetas] (contributor)
+KITAITI Makoto [KitaitiMakoto] (contributor)
+Jacob Floyd [cognifloyd] (contributor)
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/LICENSE.txt b/library/vendor/dompdf/vendor/masterminds/html5/LICENSE.txt
new file mode 100644
index 000000000..3c275b54a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/LICENSE.txt
@@ -0,0 +1,66 @@
+## HTML5-PHP License
+
+Copyright (c) 2013 The Authors of HTML5-PHP
+
+Matt Butcher - mattbutcher@google.com
+Matt Farina - matt@mattfarina.com
+Asmir Mustafic - goetas@gmail.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+## HTML5Lib License
+
+Portions of this are based on html5lib's PHP version, which was a
+sub-project of html5lib. The following is the list of contributors from
+html5lib:
+
+html5lib:
+
+Copyright (c) 2006-2009 The Authors
+
+Contributors:
+James Graham - jg307@cam.ac.uk
+Anne van Kesteren - annevankesteren@gmail.com
+Lachlan Hunt - lachlan.hunt@lachy.id.au
+Matt McDonald - kanashii@kanashii.ca
+Sam Ruby - rubys@intertwingly.net
+Ian Hickson (Google) - ian@hixie.ch
+Thomas Broyer - t.broyer@ltgt.net
+Jacques Distler - distler@golem.ph.utexas.edu
+Henri Sivonen - hsivonen@iki.fi
+Adam Barth - abarth@webkit.org
+Eric Seidel - eric@webkit.org
+The Mozilla Foundation (contributions from Henri Sivonen since 2008)
+David Flanagan (Mozilla) - dflanagan@mozilla.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/README.md b/library/vendor/dompdf/vendor/masterminds/html5/README.md
new file mode 100644
index 000000000..b1ca1e371
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/README.md
@@ -0,0 +1,270 @@
+> # UKRAINE NEEDS YOUR HELP NOW!
+>
+> On 24 February 2022, Russian [President Vladimir Putin ordered an invasion of Ukraine by Russian Armed Forces](https://www.bbc.com/news/world-europe-60504334).
+>
+> Your support is urgently needed.
+>
+> - Donate to the volunteers. Here is the volunteer fund helping the Ukrainian army to provide all the necessary equipment:
+> https://bank.gov.ua/en/news/all/natsionalniy-bank-vidkriv-spetsrahunok-dlya-zboru-koshtiv-na-potrebi-armiyi or https://savelife.in.ua/en/donate/
+> - Triple-check social media sources. Russian disinformation is attempting to coverup and distort the reality in Ukraine.
+> - Help Ukrainian refugees who are fleeing Russian attacks and shellings: https://www.globalcitizen.org/en/content/ways-to-help-ukraine-conflict/
+> - Put pressure on your political representatives to provide help to Ukraine.
+> - Believe in the Ukrainian people, they will not surrender, they don't have another Ukraine.
+>
+> THANK YOU!
+----
+
+# HTML5-PHP
+
+HTML5 is a standards-compliant HTML5 parser and writer written entirely in PHP.
+It is stable and used in many production websites, and has
+well over [five million downloads](https://packagist.org/packages/masterminds/html5).
+
+HTML5 provides the following features.
+
+- An HTML5 serializer
+- Support for PHP namespaces
+- Composer support
+- Event-based (SAX-like) parser
+- A DOM tree builder
+- Interoperability with [QueryPath](https://github.com/technosophos/querypath)
+- Runs on **PHP** 5.3.0 or newer
+
+[](https://travis-ci.org/Masterminds/html5-php)
+[](https://packagist.org/packages/masterminds/html5)
+[](https://scrutinizer-ci.com/g/Masterminds/html5-php/?branch=master)
+[](https://scrutinizer-ci.com/g/Masterminds/html5-php/?branch=master)
+[](https://masterminds.github.io/stability/sustained.html)
+
+## Installation
+
+Install HTML5-PHP using [composer](http://getcomposer.org/).
+
+By adding the `masterminds/html5` dependency to your `composer.json` file:
+
+```json
+{
+ "require" : {
+ "masterminds/html5": "^2.0"
+ },
+}
+```
+
+By invoking require command via composer executable:
+
+```bash
+composer require masterminds/html5
+```
+
+## Basic Usage
+
+HTML5-PHP has a high-level API and a low-level API.
+
+Here is how you use the high-level `HTML5` library API:
+
+```php
+
+
+ TEST
+
+
+ Hello World
+ This is a test of the HTML5 parser.
+
+
+HERE;
+
+// Parse the document. $dom is a DOMDocument.
+$html5 = new HTML5();
+$dom = $html5->loadHTML($html);
+
+// Render it as HTML5:
+print $html5->saveHTML($dom);
+
+// Or save it to a file:
+$html5->save($dom, 'out.html');
+```
+
+The `$dom` created by the parser is a full `DOMDocument` object. And the
+`save()` and `saveHTML()` methods will take any DOMDocument.
+
+### Options
+
+It is possible to pass in an array of configuration options when loading
+an HTML5 document.
+
+```php
+// An associative array of options
+$options = array(
+ 'option_name' => 'option_value',
+);
+
+// Provide the options to the constructor
+$html5 = new HTML5($options);
+
+$dom = $html5->loadHTML($html);
+```
+
+The following options are supported:
+
+* `encode_entities` (boolean): Indicates that the serializer should aggressively
+ encode characters as entities. Without this, it only encodes the bare
+ minimum.
+* `disable_html_ns` (boolean): Prevents the parser from automatically
+ assigning the HTML5 namespace to the DOM document. This is for
+ non-namespace aware DOM tools.
+* `target_document` (\DOMDocument): A DOM document that will be used as the
+ destination for the parsed nodes.
+* `implicit_namespaces` (array): An assoc array of namespaces that should be
+ used by the parser. Name is tag prefix, value is NS URI.
+
+## The Low-Level API
+
+This library provides the following low-level APIs that you can use to
+create more customized HTML5 tools:
+
+- A SAX-like event-based parser that you can hook into for special kinds
+of parsing.
+- A flexible error-reporting mechanism that can be tuned to document
+syntax checking.
+- A DOM implementation that uses PHP's built-in DOM library.
+
+The unit tests exercise each piece of the API, and every public function
+is well-documented.
+
+### Parser Design
+
+The parser is designed as follows:
+
+- The `Scanner` handles scanning on behalf of the parser.
+- The `Tokenizer` requests data off of the scanner, parses it, clasifies
+it, and sends it to an `EventHandler`. It is a *recursive descent parser.*
+- The `EventHandler` receives notifications and data for each specific
+semantic event that occurs during tokenization.
+- The `DOMBuilder` is an `EventHandler` that listens for tokenizing
+events and builds a document tree (`DOMDocument`) based on the events.
+
+### Serializer Design
+
+The serializer takes a data structure (the `DOMDocument`) and transforms
+it into a character representation -- an HTML5 document.
+
+The serializer is broken into three parts:
+
+- The `OutputRules` contain the rules to turn DOM elements into strings. The
+rules are an implementation of the interface `RulesInterface` allowing for
+different rule sets to be used.
+- The `Traverser`, which is a special-purpose tree walker. It visits
+each node node in the tree and uses the `OutputRules` to transform the node
+into a string.
+- `HTML5` manages the `Traverser` and stores the resultant data
+in the correct place.
+
+The serializer (`save()`, `saveHTML()`) follows the
+[section 8.9 of the HTML 5.0 spec](http://www.w3.org/TR/2012/CR-html5-20121217/syntax.html#serializing-html-fragments).
+So tags are serialized according to these rules:
+
+- A tag with children: <foo>CHILDREN</foo>
+- A tag that cannot have content: <foo> (no closing tag)
+- A tag that could have content, but doesn't: <foo></foo>
+
+## Known Issues (Or, Things We Designed Against the Spec)
+
+Please check the issue queue for a full list, but the following are
+issues known issues that are not presently on the roadmap:
+
+- Namespaces: HTML5 only [supports a selected list of namespaces](http://www.w3.org/TR/html5/infrastructure.html#namespaces)
+ and they do not operate in the same way as XML namespaces. A `:` has no special
+ meaning.
+ By default the parser does not support XML style namespaces via `:`;
+ to enable the XML namespaces see the [XML Namespaces section](#xml-namespaces)
+- Scripts: This parser does not contain a JavaScript or a CSS
+ interpreter. While one may be supplied, not all features will be
+ supported.
+- Rentrance: The current parser is not re-entrant. (Thus you can't pause
+ the parser to modify the HTML string mid-parse.)
+- Validation: The current tree builder is **not** a validating parser.
+ While it will correct some HTML, it does not check that the HTML
+ conforms to the standard. (Should you wish, you can build a validating
+ parser by extending DOMTree or building your own EventHandler
+ implementation.)
+ * There is limited support for insertion modes.
+ * Some autocorrection is done automatically.
+ * Per the spec, many legacy tags are admitted and correctly handled,
+ even though they are technically not part of HTML5.
+- Attribute names and values: Due to the implementation details of the
+ PHP implementation of DOM, attribute names that do not follow the
+ XML 1.0 standard are not inserted into the DOM. (Effectively, they
+ are ignored.) If you've got a clever fix for this, jump in!
+- Processor Instructions: The HTML5 spec does not allow processor
+ instructions. We do. Since this is a server-side library, we think
+ this is useful. And that means, dear reader, that in some cases you
+ can parse the HTML from a mixed PHP/HTML document. This, however,
+ is an incidental feature, not a core feature.
+- HTML manifests: Unsupported.
+- PLAINTEXT: Unsupported.
+- Adoption Agency Algorithm: Not yet implemented. (8.2.5.4.7)
+
+## XML Namespaces
+
+To use XML style namespaces you have to configure well the main `HTML5` instance.
+
+```php
+use Masterminds\HTML5;
+$html = new HTML5(array(
+ "xmlNamespaces" => true
+));
+
+$dom = $html->loadHTML(' ');
+
+$dom->documentElement->namespaceURI; // http://www.example.com
+
+```
+
+You can also add some default prefixes that will not require the namespace declaration,
+but its elements will be namespaced.
+
+```php
+use Masterminds\HTML5;
+$html = new HTML5(array(
+ "implicitNamespaces"=>array(
+ "t"=>"http://www.example.com"
+ )
+));
+
+$dom = $html->loadHTML(' ');
+
+$dom->documentElement->namespaceURI; // http://www.example.com
+
+```
+
+## Thanks to...
+
+The dedicated (and patient) contributors of patches small and large,
+who have already made this library better.See the CREDITS file for
+a list of contributors.
+
+We owe a huge debt of gratitude to the original authors of html5lib.
+
+While not much of the original parser remains, we learned a lot from
+reading the html5lib library. And some pieces remain here. In
+particular, much of the UTF-8 and Unicode handling is derived from the
+html5lib project.
+
+## License
+
+This software is released under the MIT license. The original html5lib
+library was also released under the MIT license.
+
+See LICENSE.txt
+
+Certain files contain copyright assertions by specific individuals
+involved with html5lib. Those have been retained where appropriate.
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/RELEASE.md b/library/vendor/dompdf/vendor/masterminds/html5/RELEASE.md
new file mode 100644
index 000000000..33007ed69
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/RELEASE.md
@@ -0,0 +1,157 @@
+# Release Notes
+
+2.7.6 (2021-08-18)
+
+- #218: Address comment handling issues
+
+2.7.5 (2021-07-01)
+
+- #204: Travis: Enable tests on PHP 8.0
+- #207: Fix PHP 8.1 deprecations
+
+2.7.4 (2020-10-01)
+
+- #191: Fix travisci build
+- #195: Add .gitattributes file with export-ignore rules
+- #194: Fix query parameter parsed as character entity
+
+2.7.3 (2020-07-05)
+
+- #190: mitigate cyclic reference between output rules and the traverser objects
+
+2.7.2 (2020-07-01)
+
+- #187: Fixed memory leak in HTML5::saveHTML()
+- #186: Add special case for end tag
+
+2.7.1 (2020-06-14)
+
+- #171: add PHP 7.4 job
+- #178: Prevent infinite loop on un-terminated entity declaration at EOF
+
+2.7.0 (2019-07-25)
+
+- #164: Drop HHVM support
+- #168: Set default encoding in the DOMDocument object
+
+2.6.0 (2019-03-10)
+
+- #163: Allow to pass a charset to the Scanner
+
+2.5.0 (2018-12-27)
+
+- #162, #161, #155, #154, #153, #151: big performance improvements
+- #156: fixed typos
+- #160: adopt and enforce code style
+- #159: remove deprecated php unit base test case
+- #150: backport changes from old master branch
+
+2.4.0 (2018-11-17)
+
+- #148: Improve performance by moving sequence matching
+- #147: Improve the Tokenizer performance
+- #146: Improve performance by relying on a native string instead of InputStream
+- #144: Add DOM extension in composer.json
+- #145: Add more extensions on composer.json, improve phpdocs and remove dead code
+- #143: Remove experimental comment
+
+2.3.1 (2018-10-18)
+
+- #121: Audio is not a block tag (fixed by #141)
+- #136: Handle illegal self-closing according to spec (fixed by #137)
+- #141: Minor fixes in the README
+
+2.3.0 (2017-09-04)
+
+- #129: image within inline svg breaks system (fixed by #133)
+- #131: ² does not work (fixed by #132)
+- #134: Improve tokenizer performance by 20% (alternative version of #130 thanks to @MichaelHeerklotz)
+- #135: Raw & in attributes
+
+2.2.2 (2016-09-22)
+
+- #116: In XML mode, tags are case sensitive
+- #115: Fix PHP Notice in OutputRules
+- #112: fix parsing of options of an optgroup
+- #111: Adding test for the address tag
+
+2.2.1 (2016-05-10)
+
+- #109: Fixed issue where address tag could be written without closing tag (thanks sylus)
+
+2.2.0 (2016-04-11)
+
+- #105: Enable composer cache (for CI/CD)
+- #100: Use mb_substitute_character inset of ini_set for environments where ini_set is disable (e.g., shared hosting)
+- #98: Allow link, meta, style tags in noscript tags
+- #96: Fixed xml:href on svgs that use the "use" breaking
+- #94: Counting UTF8 characters performance improvement
+- #93: Use newer version of coveralls package
+- #90: Remove duplicate test
+- #87: Allow multiple root nodes
+
+2.1.2 (2015-06-07)
+- #82: Support for PHP7
+- #84: Improved boolean attribute handling
+
+2.1.1 (2015-03-23)
+- #78: Fixes bug where unmatched entity like string drops everything after &.
+
+2.1.0 (2015-02-01)
+- #74: Added `disable_html_ns` and `target_doc` dom parsing options
+- Unified option names
+- #73: Fixed alphabet, ß now can be detected
+- #75 and #76: Allow whitespace in RCDATA tags
+- #77: Fixed parsing blunder for json embeds
+- #72: Add options to HTML methods
+
+2.0.2 (2014-12-17)
+- #50: empty document handling
+- #63: tags with strange capitalization
+- #65: dashes and underscores as allowed characters in tag names
+- #68: Fixed issue with non-inline elements inside inline containers
+
+2.0.1 (2014-09-23)
+- #59: Fixed issue parsing some fragments.
+- #56: Incorrectly saw 0 as empty string
+- Sami as new documentation generator
+
+2.0.0 (2014-07-28)
+- #53: Improved boolean attributes handling
+- #52: Facebook HHVM compatibility
+- #48: Adopted PSR-2 as coding standard
+- #47: Moved everything to Masterminds namespace
+- #45: Added custom namespaces
+- #44: Added support to XML-style namespaces
+- #37: Refactored HTML5 class removing static methods
+
+1.0.5 (2014-06-10)
+- #38: Set the dev-master branch as the 1.0.x branch for composer (goetas)
+- #34: Tests use PSR-4 for autoloading. (goetas)
+- #40, #41: Fix entity handling in RCDATA sections. (KitaitiMakoto)
+- #32: Fixed issue where wharacter references were being incorrectly encoded in style tags.
+
+1.0.4 (2014-04-29)
+- #30/#31 Don't throw an exception for invalid tag names.
+
+1.0.3 (2014-02-28)
+- #23 and #29: Ignore attributes with illegal chars in name for the PHP DOM.
+
+1.0.2 (2014-02-12)
+- #23: Handle missing tag close in attribute list.
+- #25: Fixed text escaping in the serializer (HTML% 8.3).
+- #27: Fixed tests on Windows: changed "\n" -> PHP_EOL.
+- #28: Fixed infinite loop for char "&" in unquoted attribute in parser.
+- #26: Updated tag name case handling to deal with uppercase usage.
+- #24: Newlines and tabs are allowed inside quoted attributes (HTML5 8.2.4).
+- Fixed Travis CI testing.
+
+1.0.1 (2013-11-07)
+- CDATA encoding is improved. (Non-standard; Issue #19)
+- Some parser rules were not returning the new current element. (Issue #20)
+- Added, to the README, details on code test coverage and to packagist version.
+- Fixed processor instructions.
+- Improved test coverage and documentation coverage.
+
+1.0.0 (2013-10-02)
+- Initial release.
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/UPGRADING.md b/library/vendor/dompdf/vendor/masterminds/html5/UPGRADING.md
new file mode 100644
index 000000000..76e3a19bc
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/UPGRADING.md
@@ -0,0 +1,21 @@
+From 1.x to 2.x
+=================
+
+- All classes uses `Masterminds` namespace.
+- All public static methods has been removed from `HTML5` class and the general API to access the HTML5 functionalities has changed.
+
+ Before:
+
+ $dom = \HTML5::loadHTML('....');
+ \HTML5::saveHTML($dom);
+
+ After:
+
+ use Masterminds\HTML5;
+
+ $html5 = new HTML5();
+
+ $dom = $html5->loadHTML('....');
+ echo $html5->saveHTML($dom);
+
+
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/bin/entities.php b/library/vendor/dompdf/vendor/masterminds/html5/bin/entities.php
new file mode 100644
index 000000000..56323a341
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/bin/entities.php
@@ -0,0 +1,26 @@
+ $obj) {
+ $sname = substr($name, 1, -1);
+ $table[$sname] = $obj->characters;
+}
+
+echo '=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit" : "^4.8.35 || ^5.7.21 || ^6 || ^7"
+ },
+ "autoload": {
+ "psr-4": {"Masterminds\\": "src"}
+ },
+ "autoload-dev": {
+ "psr-4": {"Masterminds\\HTML5\\Tests\\": "test/HTML5"}
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5.php
new file mode 100644
index 000000000..c857145fb
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5.php
@@ -0,0 +1,246 @@
+ false,
+
+ // Prevents the parser from automatically assigning the HTML5 namespace to the DOM document.
+ 'disable_html_ns' => false,
+ );
+
+ protected $errors = array();
+
+ public function __construct(array $defaultOptions = array())
+ {
+ $this->defaultOptions = array_merge($this->defaultOptions, $defaultOptions);
+ }
+
+ /**
+ * Get the current default options.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->defaultOptions;
+ }
+
+ /**
+ * Load and parse an HTML file.
+ *
+ * This will apply the HTML5 parser, which is tolerant of many
+ * varieties of HTML, including XHTML 1, HTML 4, and well-formed HTML
+ * 3. Note that in these cases, not all of the old data will be
+ * preserved. For example, XHTML's XML declaration will be removed.
+ *
+ * The rules governing parsing are set out in the HTML 5 spec.
+ *
+ * @param string|resource $file The path to the file to parse. If this is a resource, it is
+ * assumed to be an open stream whose pointer is set to the first
+ * byte of input.
+ * @param array $options Configuration options when parsing the HTML.
+ *
+ * @return \DOMDocument A DOM document. These object type is defined by the libxml
+ * library, and should have been included with your version of PHP.
+ */
+ public function load($file, array $options = array())
+ {
+ // Handle the case where file is a resource.
+ if (is_resource($file)) {
+ return $this->parse(stream_get_contents($file), $options);
+ }
+
+ return $this->parse(file_get_contents($file), $options);
+ }
+
+ /**
+ * Parse a HTML Document from a string.
+ *
+ * Take a string of HTML 5 (or earlier) and parse it into a
+ * DOMDocument.
+ *
+ * @param string $string A html5 document as a string.
+ * @param array $options Configuration options when parsing the HTML.
+ *
+ * @return \DOMDocument A DOM document. DOM is part of libxml, which is included with
+ * almost all distribtions of PHP.
+ */
+ public function loadHTML($string, array $options = array())
+ {
+ return $this->parse($string, $options);
+ }
+
+ /**
+ * Convenience function to load an HTML file.
+ *
+ * This is here to provide backwards compatibility with the
+ * PHP DOM implementation. It simply calls load().
+ *
+ * @param string $file The path to the file to parse. If this is a resource, it is
+ * assumed to be an open stream whose pointer is set to the first
+ * byte of input.
+ * @param array $options Configuration options when parsing the HTML.
+ *
+ * @return \DOMDocument A DOM document. These object type is defined by the libxml
+ * library, and should have been included with your version of PHP.
+ */
+ public function loadHTMLFile($file, array $options = array())
+ {
+ return $this->load($file, $options);
+ }
+
+ /**
+ * Parse a HTML fragment from a string.
+ *
+ * @param string $string the HTML5 fragment as a string
+ * @param array $options Configuration options when parsing the HTML
+ *
+ * @return \DOMDocumentFragment A DOM fragment. The DOM is part of libxml, which is included with
+ * almost all distributions of PHP.
+ */
+ public function loadHTMLFragment($string, array $options = array())
+ {
+ return $this->parseFragment($string, $options);
+ }
+
+ /**
+ * Return all errors encountered into parsing phase.
+ *
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * Return true it some errors were encountered into parsing phase.
+ *
+ * @return bool
+ */
+ public function hasErrors()
+ {
+ return count($this->errors) > 0;
+ }
+
+ /**
+ * Parse an input string.
+ *
+ * @param string $input
+ * @param array $options
+ *
+ * @return \DOMDocument
+ */
+ public function parse($input, array $options = array())
+ {
+ $this->errors = array();
+ $options = array_merge($this->defaultOptions, $options);
+ $events = new DOMTreeBuilder(false, $options);
+ $scanner = new Scanner($input, !empty($options['encoding']) ? $options['encoding'] : 'UTF-8');
+ $parser = new Tokenizer($scanner, $events, !empty($options['xmlNamespaces']) ? Tokenizer::CONFORMANT_XML : Tokenizer::CONFORMANT_HTML);
+
+ $parser->parse();
+ $this->errors = $events->getErrors();
+
+ return $events->document();
+ }
+
+ /**
+ * Parse an input stream where the stream is a fragment.
+ *
+ * Lower-level loading function. This requires an input stream instead
+ * of a string, file, or resource.
+ *
+ * @param string $input The input data to parse in the form of a string.
+ * @param array $options An array of options.
+ *
+ * @return \DOMDocumentFragment
+ */
+ public function parseFragment($input, array $options = array())
+ {
+ $options = array_merge($this->defaultOptions, $options);
+ $events = new DOMTreeBuilder(true, $options);
+ $scanner = new Scanner($input, !empty($options['encoding']) ? $options['encoding'] : 'UTF-8');
+ $parser = new Tokenizer($scanner, $events, !empty($options['xmlNamespaces']) ? Tokenizer::CONFORMANT_XML : Tokenizer::CONFORMANT_HTML);
+
+ $parser->parse();
+ $this->errors = $events->getErrors();
+
+ return $events->fragment();
+ }
+
+ /**
+ * Save a DOM into a given file as HTML5.
+ *
+ * @param mixed $dom The DOM to be serialized.
+ * @param string|resource $file The filename to be written or resource to write to.
+ * @param array $options Configuration options when serializing the DOM. These include:
+ * - encode_entities: Text written to the output is escaped by default and not all
+ * entities are encoded. If this is set to true all entities will be encoded.
+ * Defaults to false.
+ */
+ public function save($dom, $file, $options = array())
+ {
+ $close = true;
+ if (is_resource($file)) {
+ $stream = $file;
+ $close = false;
+ } else {
+ $stream = fopen($file, 'wb');
+ }
+ $options = array_merge($this->defaultOptions, $options);
+ $rules = new OutputRules($stream, $options);
+ $trav = new Traverser($dom, $stream, $rules, $options);
+
+ $trav->walk();
+ /*
+ * release the traverser to avoid cyclic references and allow PHP to free memory without waiting for gc_collect_cycles
+ */
+ $rules->unsetTraverser();
+ if ($close) {
+ fclose($stream);
+ }
+ }
+
+ /**
+ * Convert a DOM into an HTML5 string.
+ *
+ * @param mixed $dom The DOM to be serialized.
+ * @param array $options Configuration options when serializing the DOM. These include:
+ * - encode_entities: Text written to the output is escaped by default and not all
+ * entities are encoded. If this is set to true all entities will be encoded.
+ * Defaults to false.
+ *
+ * @return string A HTML5 documented generated from the DOM.
+ */
+ public function saveHTML($dom, $options = array())
+ {
+ $stream = fopen('php://temp', 'wb');
+ $this->save($dom, $stream, array_merge($this->defaultOptions, $options));
+
+ $html = stream_get_contents($stream, -1, 0);
+
+ fclose($stream);
+
+ return $html;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Elements.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Elements.php
new file mode 100644
index 000000000..8fe798789
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Elements.php
@@ -0,0 +1,619 @@
+ 1,
+ 'abbr' => 1,
+ 'address' => 65, // NORMAL | BLOCK_TAG
+ 'area' => 9, // NORMAL | VOID_TAG
+ 'article' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'aside' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'audio' => 1, // NORMAL
+ 'b' => 1,
+ 'base' => 9, // NORMAL | VOID_TAG
+ 'bdi' => 1,
+ 'bdo' => 1,
+ 'blockquote' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'body' => 1,
+ 'br' => 9, // NORMAL | VOID_TAG
+ 'button' => 1,
+ 'canvas' => 65, // NORMAL | BLOCK_TAG
+ 'caption' => 1,
+ 'cite' => 1,
+ 'code' => 1,
+ 'col' => 9, // NORMAL | VOID_TAG
+ 'colgroup' => 1,
+ 'command' => 9, // NORMAL | VOID_TAG
+ // "data" => 1, // This is highly experimental and only part of the whatwg spec (not w3c). See https://developer.mozilla.org/en-US/docs/HTML/Element/data
+ 'datalist' => 1,
+ 'dd' => 65, // NORMAL | BLOCK_TAG
+ 'del' => 1,
+ 'details' => 17, // NORMAL | AUTOCLOSE_P,
+ 'dfn' => 1,
+ 'dialog' => 17, // NORMAL | AUTOCLOSE_P,
+ 'div' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'dl' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'dt' => 1,
+ 'em' => 1,
+ 'embed' => 9, // NORMAL | VOID_TAG
+ 'fieldset' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'figcaption' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'figure' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'footer' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'form' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h1' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h2' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h3' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h4' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h5' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h6' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'head' => 1,
+ 'header' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'hgroup' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'hr' => 73, // NORMAL | VOID_TAG
+ 'html' => 1,
+ 'i' => 1,
+ 'iframe' => 3, // NORMAL | TEXT_RAW
+ 'img' => 9, // NORMAL | VOID_TAG
+ 'input' => 9, // NORMAL | VOID_TAG
+ 'kbd' => 1,
+ 'ins' => 1,
+ 'keygen' => 9, // NORMAL | VOID_TAG
+ 'label' => 1,
+ 'legend' => 1,
+ 'li' => 1,
+ 'link' => 9, // NORMAL | VOID_TAG
+ 'map' => 1,
+ 'mark' => 1,
+ 'menu' => 17, // NORMAL | AUTOCLOSE_P,
+ 'meta' => 9, // NORMAL | VOID_TAG
+ 'meter' => 1,
+ 'nav' => 17, // NORMAL | AUTOCLOSE_P,
+ 'noscript' => 65, // NORMAL | BLOCK_TAG
+ 'object' => 1,
+ 'ol' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'optgroup' => 1,
+ 'option' => 1,
+ 'output' => 65, // NORMAL | BLOCK_TAG
+ 'p' => 209, // NORMAL | AUTOCLOSE_P | BLOCK_TAG | BLOCK_ONLY_INLINE
+ 'param' => 9, // NORMAL | VOID_TAG
+ 'pre' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'progress' => 1,
+ 'q' => 1,
+ 'rp' => 1,
+ 'rt' => 1,
+ 'ruby' => 1,
+ 's' => 1,
+ 'samp' => 1,
+ 'script' => 3, // NORMAL | TEXT_RAW
+ 'section' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'select' => 1,
+ 'small' => 1,
+ 'source' => 9, // NORMAL | VOID_TAG
+ 'span' => 1,
+ 'strong' => 1,
+ 'style' => 3, // NORMAL | TEXT_RAW
+ 'sub' => 1,
+ 'summary' => 17, // NORMAL | AUTOCLOSE_P,
+ 'sup' => 1,
+ 'table' => 65, // NORMAL | BLOCK_TAG
+ 'tbody' => 1,
+ 'td' => 1,
+ 'textarea' => 5, // NORMAL | TEXT_RCDATA
+ 'tfoot' => 65, // NORMAL | BLOCK_TAG
+ 'th' => 1,
+ 'thead' => 1,
+ 'time' => 1,
+ 'title' => 5, // NORMAL | TEXT_RCDATA
+ 'tr' => 1,
+ 'track' => 9, // NORMAL | VOID_TAG
+ 'u' => 1,
+ 'ul' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'var' => 1,
+ 'video' => 65, // NORMAL | BLOCK_TAG
+ 'wbr' => 9, // NORMAL | VOID_TAG
+
+ // Legacy?
+ 'basefont' => 8, // VOID_TAG
+ 'bgsound' => 8, // VOID_TAG
+ 'noframes' => 2, // RAW_TEXT
+ 'frame' => 9, // NORMAL | VOID_TAG
+ 'frameset' => 1,
+ 'center' => 16,
+ 'dir' => 16,
+ 'listing' => 16, // AUTOCLOSE_P
+ 'plaintext' => 48, // AUTOCLOSE_P | TEXT_PLAINTEXT
+ 'applet' => 0,
+ 'marquee' => 0,
+ 'isindex' => 8, // VOID_TAG
+ 'xmp' => 20, // AUTOCLOSE_P | VOID_TAG | RAW_TEXT
+ 'noembed' => 2, // RAW_TEXT
+ );
+
+ /**
+ * The MathML elements.
+ * See http://www.w3.org/wiki/MathML/Elements.
+ *
+ * In our case we are only concerned with presentation MathML and not content
+ * MathML. There is a nice list of this subset at https://developer.mozilla.org/en-US/docs/MathML/Element.
+ *
+ * @var array
+ */
+ public static $mathml = array(
+ 'maction' => 1,
+ 'maligngroup' => 1,
+ 'malignmark' => 1,
+ 'math' => 1,
+ 'menclose' => 1,
+ 'merror' => 1,
+ 'mfenced' => 1,
+ 'mfrac' => 1,
+ 'mglyph' => 1,
+ 'mi' => 1,
+ 'mlabeledtr' => 1,
+ 'mlongdiv' => 1,
+ 'mmultiscripts' => 1,
+ 'mn' => 1,
+ 'mo' => 1,
+ 'mover' => 1,
+ 'mpadded' => 1,
+ 'mphantom' => 1,
+ 'mroot' => 1,
+ 'mrow' => 1,
+ 'ms' => 1,
+ 'mscarries' => 1,
+ 'mscarry' => 1,
+ 'msgroup' => 1,
+ 'msline' => 1,
+ 'mspace' => 1,
+ 'msqrt' => 1,
+ 'msrow' => 1,
+ 'mstack' => 1,
+ 'mstyle' => 1,
+ 'msub' => 1,
+ 'msup' => 1,
+ 'msubsup' => 1,
+ 'mtable' => 1,
+ 'mtd' => 1,
+ 'mtext' => 1,
+ 'mtr' => 1,
+ 'munder' => 1,
+ 'munderover' => 1,
+ );
+
+ /**
+ * The svg elements.
+ *
+ * The Mozilla documentation has a good list at https://developer.mozilla.org/en-US/docs/SVG/Element.
+ * The w3c list appears to be lacking in some areas like filter effect elements.
+ * That list can be found at http://www.w3.org/wiki/SVG/Elements.
+ *
+ * Note, FireFox appears to do a better job rendering filter effects than chrome.
+ * While they are in the spec I'm not sure how widely implemented they are.
+ *
+ * @var array
+ */
+ public static $svg = array(
+ 'a' => 1,
+ 'altGlyph' => 1,
+ 'altGlyphDef' => 1,
+ 'altGlyphItem' => 1,
+ 'animate' => 1,
+ 'animateColor' => 1,
+ 'animateMotion' => 1,
+ 'animateTransform' => 1,
+ 'circle' => 1,
+ 'clipPath' => 1,
+ 'color-profile' => 1,
+ 'cursor' => 1,
+ 'defs' => 1,
+ 'desc' => 1,
+ 'ellipse' => 1,
+ 'feBlend' => 1,
+ 'feColorMatrix' => 1,
+ 'feComponentTransfer' => 1,
+ 'feComposite' => 1,
+ 'feConvolveMatrix' => 1,
+ 'feDiffuseLighting' => 1,
+ 'feDisplacementMap' => 1,
+ 'feDistantLight' => 1,
+ 'feFlood' => 1,
+ 'feFuncA' => 1,
+ 'feFuncB' => 1,
+ 'feFuncG' => 1,
+ 'feFuncR' => 1,
+ 'feGaussianBlur' => 1,
+ 'feImage' => 1,
+ 'feMerge' => 1,
+ 'feMergeNode' => 1,
+ 'feMorphology' => 1,
+ 'feOffset' => 1,
+ 'fePointLight' => 1,
+ 'feSpecularLighting' => 1,
+ 'feSpotLight' => 1,
+ 'feTile' => 1,
+ 'feTurbulence' => 1,
+ 'filter' => 1,
+ 'font' => 1,
+ 'font-face' => 1,
+ 'font-face-format' => 1,
+ 'font-face-name' => 1,
+ 'font-face-src' => 1,
+ 'font-face-uri' => 1,
+ 'foreignObject' => 1,
+ 'g' => 1,
+ 'glyph' => 1,
+ 'glyphRef' => 1,
+ 'hkern' => 1,
+ 'image' => 1,
+ 'line' => 1,
+ 'linearGradient' => 1,
+ 'marker' => 1,
+ 'mask' => 1,
+ 'metadata' => 1,
+ 'missing-glyph' => 1,
+ 'mpath' => 1,
+ 'path' => 1,
+ 'pattern' => 1,
+ 'polygon' => 1,
+ 'polyline' => 1,
+ 'radialGradient' => 1,
+ 'rect' => 1,
+ 'script' => 3, // NORMAL | RAW_TEXT
+ 'set' => 1,
+ 'stop' => 1,
+ 'style' => 3, // NORMAL | RAW_TEXT
+ 'svg' => 1,
+ 'switch' => 1,
+ 'symbol' => 1,
+ 'text' => 1,
+ 'textPath' => 1,
+ 'title' => 1,
+ 'tref' => 1,
+ 'tspan' => 1,
+ 'use' => 1,
+ 'view' => 1,
+ 'vkern' => 1,
+ );
+
+ /**
+ * Some attributes in SVG are case sensitive.
+ *
+ * This map contains key/value pairs with the key as the lowercase attribute
+ * name and the value with the correct casing.
+ */
+ public static $svgCaseSensitiveAttributeMap = array(
+ 'attributename' => 'attributeName',
+ 'attributetype' => 'attributeType',
+ 'basefrequency' => 'baseFrequency',
+ 'baseprofile' => 'baseProfile',
+ 'calcmode' => 'calcMode',
+ 'clippathunits' => 'clipPathUnits',
+ 'contentscripttype' => 'contentScriptType',
+ 'contentstyletype' => 'contentStyleType',
+ 'diffuseconstant' => 'diffuseConstant',
+ 'edgemode' => 'edgeMode',
+ 'externalresourcesrequired' => 'externalResourcesRequired',
+ 'filterres' => 'filterRes',
+ 'filterunits' => 'filterUnits',
+ 'glyphref' => 'glyphRef',
+ 'gradienttransform' => 'gradientTransform',
+ 'gradientunits' => 'gradientUnits',
+ 'kernelmatrix' => 'kernelMatrix',
+ 'kernelunitlength' => 'kernelUnitLength',
+ 'keypoints' => 'keyPoints',
+ 'keysplines' => 'keySplines',
+ 'keytimes' => 'keyTimes',
+ 'lengthadjust' => 'lengthAdjust',
+ 'limitingconeangle' => 'limitingConeAngle',
+ 'markerheight' => 'markerHeight',
+ 'markerunits' => 'markerUnits',
+ 'markerwidth' => 'markerWidth',
+ 'maskcontentunits' => 'maskContentUnits',
+ 'maskunits' => 'maskUnits',
+ 'numoctaves' => 'numOctaves',
+ 'pathlength' => 'pathLength',
+ 'patterncontentunits' => 'patternContentUnits',
+ 'patterntransform' => 'patternTransform',
+ 'patternunits' => 'patternUnits',
+ 'pointsatx' => 'pointsAtX',
+ 'pointsaty' => 'pointsAtY',
+ 'pointsatz' => 'pointsAtZ',
+ 'preservealpha' => 'preserveAlpha',
+ 'preserveaspectratio' => 'preserveAspectRatio',
+ 'primitiveunits' => 'primitiveUnits',
+ 'refx' => 'refX',
+ 'refy' => 'refY',
+ 'repeatcount' => 'repeatCount',
+ 'repeatdur' => 'repeatDur',
+ 'requiredextensions' => 'requiredExtensions',
+ 'requiredfeatures' => 'requiredFeatures',
+ 'specularconstant' => 'specularConstant',
+ 'specularexponent' => 'specularExponent',
+ 'spreadmethod' => 'spreadMethod',
+ 'startoffset' => 'startOffset',
+ 'stddeviation' => 'stdDeviation',
+ 'stitchtiles' => 'stitchTiles',
+ 'surfacescale' => 'surfaceScale',
+ 'systemlanguage' => 'systemLanguage',
+ 'tablevalues' => 'tableValues',
+ 'targetx' => 'targetX',
+ 'targety' => 'targetY',
+ 'textlength' => 'textLength',
+ 'viewbox' => 'viewBox',
+ 'viewtarget' => 'viewTarget',
+ 'xchannelselector' => 'xChannelSelector',
+ 'ychannelselector' => 'yChannelSelector',
+ 'zoomandpan' => 'zoomAndPan',
+ );
+
+ /**
+ * Some SVG elements are case sensitive.
+ * This map contains these.
+ *
+ * The map contains key/value store of the name is lowercase as the keys and
+ * the correct casing as the value.
+ */
+ public static $svgCaseSensitiveElementMap = array(
+ 'altglyph' => 'altGlyph',
+ 'altglyphdef' => 'altGlyphDef',
+ 'altglyphitem' => 'altGlyphItem',
+ 'animatecolor' => 'animateColor',
+ 'animatemotion' => 'animateMotion',
+ 'animatetransform' => 'animateTransform',
+ 'clippath' => 'clipPath',
+ 'feblend' => 'feBlend',
+ 'fecolormatrix' => 'feColorMatrix',
+ 'fecomponenttransfer' => 'feComponentTransfer',
+ 'fecomposite' => 'feComposite',
+ 'feconvolvematrix' => 'feConvolveMatrix',
+ 'fediffuselighting' => 'feDiffuseLighting',
+ 'fedisplacementmap' => 'feDisplacementMap',
+ 'fedistantlight' => 'feDistantLight',
+ 'feflood' => 'feFlood',
+ 'fefunca' => 'feFuncA',
+ 'fefuncb' => 'feFuncB',
+ 'fefuncg' => 'feFuncG',
+ 'fefuncr' => 'feFuncR',
+ 'fegaussianblur' => 'feGaussianBlur',
+ 'feimage' => 'feImage',
+ 'femerge' => 'feMerge',
+ 'femergenode' => 'feMergeNode',
+ 'femorphology' => 'feMorphology',
+ 'feoffset' => 'feOffset',
+ 'fepointlight' => 'fePointLight',
+ 'fespecularlighting' => 'feSpecularLighting',
+ 'fespotlight' => 'feSpotLight',
+ 'fetile' => 'feTile',
+ 'feturbulence' => 'feTurbulence',
+ 'foreignobject' => 'foreignObject',
+ 'glyphref' => 'glyphRef',
+ 'lineargradient' => 'linearGradient',
+ 'radialgradient' => 'radialGradient',
+ 'textpath' => 'textPath',
+ );
+
+ /**
+ * Check whether the given element meets the given criterion.
+ *
+ * Example:
+ *
+ * Elements::isA('script', Elements::TEXT_RAW); // Returns true.
+ *
+ * Elements::isA('script', Elements::TEXT_RCDATA); // Returns false.
+ *
+ * @param string $name The element name.
+ * @param int $mask One of the constants on this class.
+ *
+ * @return bool true if the element matches the mask, false otherwise.
+ */
+ public static function isA($name, $mask)
+ {
+ return (static::element($name) & $mask) === $mask;
+ }
+
+ /**
+ * Test if an element is a valid html5 element.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return bool true if a html5 element and false otherwise.
+ */
+ public static function isHtml5Element($name)
+ {
+ // html5 element names are case insensitive. Forcing lowercase for the check.
+ // Do we need this check or will all data passed here already be lowercase?
+ return isset(static::$html5[strtolower($name)]);
+ }
+
+ /**
+ * Test if an element name is a valid MathML presentation element.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return bool true if a MathML name and false otherwise.
+ */
+ public static function isMathMLElement($name)
+ {
+ // MathML is case-sensitive unlike html5 elements.
+ return isset(static::$mathml[$name]);
+ }
+
+ /**
+ * Test if an element is a valid SVG element.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return bool true if a SVG element and false otherise.
+ */
+ public static function isSvgElement($name)
+ {
+ // SVG is case-sensitive unlike html5 elements.
+ return isset(static::$svg[$name]);
+ }
+
+ /**
+ * Is an element name valid in an html5 document.
+ * This includes html5 elements along with other allowed embedded content
+ * such as svg and mathml.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return bool true if valid and false otherwise.
+ */
+ public static function isElement($name)
+ {
+ return static::isHtml5Element($name) || static::isMathMLElement($name) || static::isSvgElement($name);
+ }
+
+ /**
+ * Get the element mask for the given element name.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return int the element mask.
+ */
+ public static function element($name)
+ {
+ if (isset(static::$html5[$name])) {
+ return static::$html5[$name];
+ }
+ if (isset(static::$svg[$name])) {
+ return static::$svg[$name];
+ }
+ if (isset(static::$mathml[$name])) {
+ return static::$mathml[$name];
+ }
+
+ return 0;
+ }
+
+ /**
+ * Normalize a SVG element name to its proper case and form.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return string the normalized form of the element name.
+ */
+ public static function normalizeSvgElement($name)
+ {
+ $name = strtolower($name);
+ if (isset(static::$svgCaseSensitiveElementMap[$name])) {
+ $name = static::$svgCaseSensitiveElementMap[$name];
+ }
+
+ return $name;
+ }
+
+ /**
+ * Normalize a SVG attribute name to its proper case and form.
+ *
+ * @param string $name The name of the attribute.
+ *
+ * @return string The normalized form of the attribute name.
+ */
+ public static function normalizeSvgAttribute($name)
+ {
+ $name = strtolower($name);
+ if (isset(static::$svgCaseSensitiveAttributeMap[$name])) {
+ $name = static::$svgCaseSensitiveAttributeMap[$name];
+ }
+
+ return $name;
+ }
+
+ /**
+ * Normalize a MathML attribute name to its proper case and form.
+ * Note, all MathML element names are lowercase.
+ *
+ * @param string $name The name of the attribute.
+ *
+ * @return string The normalized form of the attribute name.
+ */
+ public static function normalizeMathMlAttribute($name)
+ {
+ $name = strtolower($name);
+
+ // Only one attribute has a mixed case form for MathML.
+ if ('definitionurl' === $name) {
+ $name = 'definitionURL';
+ }
+
+ return $name;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Entities.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Entities.php
new file mode 100644
index 000000000..0e7227dc1
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Entities.php
@@ -0,0 +1,2236 @@
+ 'Á',
+ 'Aacut' => 'Á',
+ 'aacute' => 'á',
+ 'aacut' => 'á',
+ 'Abreve' => 'Ă',
+ 'abreve' => 'ă',
+ 'ac' => '∾',
+ 'acd' => '∿',
+ 'acE' => '∾̳',
+ 'Acirc' => 'Â',
+ 'Acir' => 'Â',
+ 'acirc' => 'â',
+ 'acir' => 'â',
+ 'acute' => '´',
+ 'acut' => '´',
+ 'Acy' => 'А',
+ 'acy' => 'а',
+ 'AElig' => 'Æ',
+ 'AEli' => 'Æ',
+ 'aelig' => 'æ',
+ 'aeli' => 'æ',
+ 'af' => '',
+ 'Afr' => '𝔄',
+ 'afr' => '𝔞',
+ 'Agrave' => 'À',
+ 'Agrav' => 'À',
+ 'agrave' => 'à',
+ 'agrav' => 'à',
+ 'alefsym' => 'ℵ',
+ 'aleph' => 'ℵ',
+ 'Alpha' => 'Α',
+ 'alpha' => 'α',
+ 'Amacr' => 'Ā',
+ 'amacr' => 'ā',
+ 'amalg' => '⨿',
+ 'AMP' => '&',
+ 'AM' => '&',
+ 'amp' => '&',
+ 'am' => '&',
+ 'And' => '⩓',
+ 'and' => '∧',
+ 'andand' => '⩕',
+ 'andd' => '⩜',
+ 'andslope' => '⩘',
+ 'andv' => '⩚',
+ 'ang' => '∠',
+ 'ange' => '⦤',
+ 'angle' => '∠',
+ 'angmsd' => '∡',
+ 'angmsdaa' => '⦨',
+ 'angmsdab' => '⦩',
+ 'angmsdac' => '⦪',
+ 'angmsdad' => '⦫',
+ 'angmsdae' => '⦬',
+ 'angmsdaf' => '⦭',
+ 'angmsdag' => '⦮',
+ 'angmsdah' => '⦯',
+ 'angrt' => '∟',
+ 'angrtvb' => '⊾',
+ 'angrtvbd' => '⦝',
+ 'angsph' => '∢',
+ 'angst' => 'Å',
+ 'angzarr' => '⍼',
+ 'Aogon' => 'Ą',
+ 'aogon' => 'ą',
+ 'Aopf' => '𝔸',
+ 'aopf' => '𝕒',
+ 'ap' => '≈',
+ 'apacir' => '⩯',
+ 'apE' => '⩰',
+ 'ape' => '≊',
+ 'apid' => '≋',
+ 'apos' => '\'',
+ 'ApplyFunction' => '',
+ 'approx' => '≈',
+ 'approxeq' => '≊',
+ 'Aring' => 'Å',
+ 'Arin' => 'Å',
+ 'aring' => 'å',
+ 'arin' => 'å',
+ 'Ascr' => '𝒜',
+ 'ascr' => '𝒶',
+ 'Assign' => '≔',
+ 'ast' => '*',
+ 'asymp' => '≈',
+ 'asympeq' => '≍',
+ 'Atilde' => 'Ã',
+ 'Atild' => 'Ã',
+ 'atilde' => 'ã',
+ 'atild' => 'ã',
+ 'Auml' => 'Ä',
+ 'Aum' => 'Ä',
+ 'auml' => 'ä',
+ 'aum' => 'ä',
+ 'awconint' => '∳',
+ 'awint' => '⨑',
+ 'backcong' => '≌',
+ 'backepsilon' => '϶',
+ 'backprime' => '‵',
+ 'backsim' => '∽',
+ 'backsimeq' => '⋍',
+ 'Backslash' => '∖',
+ 'Barv' => '⫧',
+ 'barvee' => '⊽',
+ 'Barwed' => '⌆',
+ 'barwed' => '⌅',
+ 'barwedge' => '⌅',
+ 'bbrk' => '⎵',
+ 'bbrktbrk' => '⎶',
+ 'bcong' => '≌',
+ 'Bcy' => 'Б',
+ 'bcy' => 'б',
+ 'bdquo' => '„',
+ 'becaus' => '∵',
+ 'Because' => '∵',
+ 'because' => '∵',
+ 'bemptyv' => '⦰',
+ 'bepsi' => '϶',
+ 'bernou' => 'ℬ',
+ 'Bernoullis' => 'ℬ',
+ 'Beta' => 'Β',
+ 'beta' => 'β',
+ 'beth' => 'ℶ',
+ 'between' => '≬',
+ 'Bfr' => '𝔅',
+ 'bfr' => '𝔟',
+ 'bigcap' => '⋂',
+ 'bigcirc' => '◯',
+ 'bigcup' => '⋃',
+ 'bigodot' => '⨀',
+ 'bigoplus' => '⨁',
+ 'bigotimes' => '⨂',
+ 'bigsqcup' => '⨆',
+ 'bigstar' => '★',
+ 'bigtriangledown' => '▽',
+ 'bigtriangleup' => '△',
+ 'biguplus' => '⨄',
+ 'bigvee' => '⋁',
+ 'bigwedge' => '⋀',
+ 'bkarow' => '⤍',
+ 'blacklozenge' => '⧫',
+ 'blacksquare' => '▪',
+ 'blacktriangle' => '▴',
+ 'blacktriangledown' => '▾',
+ 'blacktriangleleft' => '◂',
+ 'blacktriangleright' => '▸',
+ 'blank' => '␣',
+ 'blk12' => '▒',
+ 'blk14' => '░',
+ 'blk34' => '▓',
+ 'block' => '█',
+ 'bne' => '=⃥',
+ 'bnequiv' => '≡⃥',
+ 'bNot' => '⫭',
+ 'bnot' => '⌐',
+ 'Bopf' => '𝔹',
+ 'bopf' => '𝕓',
+ 'bot' => '⊥',
+ 'bottom' => '⊥',
+ 'bowtie' => '⋈',
+ 'boxbox' => '⧉',
+ 'boxDL' => '╗',
+ 'boxDl' => '╖',
+ 'boxdL' => '╕',
+ 'boxdl' => '┐',
+ 'boxDR' => '╔',
+ 'boxDr' => '╓',
+ 'boxdR' => '╒',
+ 'boxdr' => '┌',
+ 'boxH' => '═',
+ 'boxh' => '─',
+ 'boxHD' => '╦',
+ 'boxHd' => '╤',
+ 'boxhD' => '╥',
+ 'boxhd' => '┬',
+ 'boxHU' => '╩',
+ 'boxHu' => '╧',
+ 'boxhU' => '╨',
+ 'boxhu' => '┴',
+ 'boxminus' => '⊟',
+ 'boxplus' => '⊞',
+ 'boxtimes' => '⊠',
+ 'boxUL' => '╝',
+ 'boxUl' => '╜',
+ 'boxuL' => '╛',
+ 'boxul' => '┘',
+ 'boxUR' => '╚',
+ 'boxUr' => '╙',
+ 'boxuR' => '╘',
+ 'boxur' => '└',
+ 'boxV' => '║',
+ 'boxv' => '│',
+ 'boxVH' => '╬',
+ 'boxVh' => '╫',
+ 'boxvH' => '╪',
+ 'boxvh' => '┼',
+ 'boxVL' => '╣',
+ 'boxVl' => '╢',
+ 'boxvL' => '╡',
+ 'boxvl' => '┤',
+ 'boxVR' => '╠',
+ 'boxVr' => '╟',
+ 'boxvR' => '╞',
+ 'boxvr' => '├',
+ 'bprime' => '‵',
+ 'Breve' => '˘',
+ 'breve' => '˘',
+ 'brvbar' => '¦',
+ 'brvba' => '¦',
+ 'Bscr' => 'ℬ',
+ 'bscr' => '𝒷',
+ 'bsemi' => '⁏',
+ 'bsim' => '∽',
+ 'bsime' => '⋍',
+ 'bsol' => '\\',
+ 'bsolb' => '⧅',
+ 'bsolhsub' => '⟈',
+ 'bull' => '•',
+ 'bullet' => '•',
+ 'bump' => '≎',
+ 'bumpE' => '⪮',
+ 'bumpe' => '≏',
+ 'Bumpeq' => '≎',
+ 'bumpeq' => '≏',
+ 'Cacute' => 'Ć',
+ 'cacute' => 'ć',
+ 'Cap' => '⋒',
+ 'cap' => '∩',
+ 'capand' => '⩄',
+ 'capbrcup' => '⩉',
+ 'capcap' => '⩋',
+ 'capcup' => '⩇',
+ 'capdot' => '⩀',
+ 'CapitalDifferentialD' => 'ⅅ',
+ 'caps' => '∩︀',
+ 'caret' => '⁁',
+ 'caron' => 'ˇ',
+ 'Cayleys' => 'ℭ',
+ 'ccaps' => '⩍',
+ 'Ccaron' => 'Č',
+ 'ccaron' => 'č',
+ 'Ccedil' => 'Ç',
+ 'Ccedi' => 'Ç',
+ 'ccedil' => 'ç',
+ 'ccedi' => 'ç',
+ 'Ccirc' => 'Ĉ',
+ 'ccirc' => 'ĉ',
+ 'Cconint' => '∰',
+ 'ccups' => '⩌',
+ 'ccupssm' => '⩐',
+ 'Cdot' => 'Ċ',
+ 'cdot' => 'ċ',
+ 'cedil' => '¸',
+ 'cedi' => '¸',
+ 'Cedilla' => '¸',
+ 'cemptyv' => '⦲',
+ 'cent' => '¢',
+ 'cen' => '¢',
+ 'CenterDot' => '·',
+ 'centerdot' => '·',
+ 'Cfr' => 'ℭ',
+ 'cfr' => '𝔠',
+ 'CHcy' => 'Ч',
+ 'chcy' => 'ч',
+ 'check' => '✓',
+ 'checkmark' => '✓',
+ 'Chi' => 'Χ',
+ 'chi' => 'χ',
+ 'cir' => '○',
+ 'circ' => 'ˆ',
+ 'circeq' => '≗',
+ 'circlearrowleft' => '↺',
+ 'circlearrowright' => '↻',
+ 'circledast' => '⊛',
+ 'circledcirc' => '⊚',
+ 'circleddash' => '⊝',
+ 'CircleDot' => '⊙',
+ 'circledR' => '®',
+ 'circledS' => 'Ⓢ',
+ 'CircleMinus' => '⊖',
+ 'CirclePlus' => '⊕',
+ 'CircleTimes' => '⊗',
+ 'cirE' => '⧃',
+ 'cire' => '≗',
+ 'cirfnint' => '⨐',
+ 'cirmid' => '⫯',
+ 'cirscir' => '⧂',
+ 'ClockwiseContourIntegral' => '∲',
+ 'CloseCurlyDoubleQuote' => '”',
+ 'CloseCurlyQuote' => '’',
+ 'clubs' => '♣',
+ 'clubsuit' => '♣',
+ 'Colon' => '∷',
+ 'colon' => ':',
+ 'Colone' => '⩴',
+ 'colone' => '≔',
+ 'coloneq' => '≔',
+ 'comma' => ',',
+ 'commat' => '@',
+ 'comp' => '∁',
+ 'compfn' => '∘',
+ 'complement' => '∁',
+ 'complexes' => 'ℂ',
+ 'cong' => '≅',
+ 'congdot' => '⩭',
+ 'Congruent' => '≡',
+ 'Conint' => '∯',
+ 'conint' => '∮',
+ 'ContourIntegral' => '∮',
+ 'Copf' => 'ℂ',
+ 'copf' => '𝕔',
+ 'coprod' => '∐',
+ 'Coproduct' => '∐',
+ 'COPY' => '©',
+ 'COP' => '©',
+ 'copy' => '©',
+ 'cop' => '©',
+ 'copysr' => '℗',
+ 'CounterClockwiseContourIntegral' => '∳',
+ 'crarr' => '↵',
+ 'Cross' => '⨯',
+ 'cross' => '✗',
+ 'Cscr' => '𝒞',
+ 'cscr' => '𝒸',
+ 'csub' => '⫏',
+ 'csube' => '⫑',
+ 'csup' => '⫐',
+ 'csupe' => '⫒',
+ 'ctdot' => '⋯',
+ 'cudarrl' => '⤸',
+ 'cudarrr' => '⤵',
+ 'cuepr' => '⋞',
+ 'cuesc' => '⋟',
+ 'cularr' => '↶',
+ 'cularrp' => '⤽',
+ 'Cup' => '⋓',
+ 'cup' => '∪',
+ 'cupbrcap' => '⩈',
+ 'CupCap' => '≍',
+ 'cupcap' => '⩆',
+ 'cupcup' => '⩊',
+ 'cupdot' => '⊍',
+ 'cupor' => '⩅',
+ 'cups' => '∪︀',
+ 'curarr' => '↷',
+ 'curarrm' => '⤼',
+ 'curlyeqprec' => '⋞',
+ 'curlyeqsucc' => '⋟',
+ 'curlyvee' => '⋎',
+ 'curlywedge' => '⋏',
+ 'curren' => '¤',
+ 'curre' => '¤',
+ 'curvearrowleft' => '↶',
+ 'curvearrowright' => '↷',
+ 'cuvee' => '⋎',
+ 'cuwed' => '⋏',
+ 'cwconint' => '∲',
+ 'cwint' => '∱',
+ 'cylcty' => '⌭',
+ 'Dagger' => '‡',
+ 'dagger' => '†',
+ 'daleth' => 'ℸ',
+ 'Darr' => '↡',
+ 'dArr' => '⇓',
+ 'darr' => '↓',
+ 'dash' => '‐',
+ 'Dashv' => '⫤',
+ 'dashv' => '⊣',
+ 'dbkarow' => '⤏',
+ 'dblac' => '˝',
+ 'Dcaron' => 'Ď',
+ 'dcaron' => 'ď',
+ 'Dcy' => 'Д',
+ 'dcy' => 'д',
+ 'DD' => 'ⅅ',
+ 'dd' => 'ⅆ',
+ 'ddagger' => '‡',
+ 'ddarr' => '⇊',
+ 'DDotrahd' => '⤑',
+ 'ddotseq' => '⩷',
+ 'deg' => '°',
+ 'de' => '°',
+ 'Del' => '∇',
+ 'Delta' => 'Δ',
+ 'delta' => 'δ',
+ 'demptyv' => '⦱',
+ 'dfisht' => '⥿',
+ 'Dfr' => '𝔇',
+ 'dfr' => '𝔡',
+ 'dHar' => '⥥',
+ 'dharl' => '⇃',
+ 'dharr' => '⇂',
+ 'DiacriticalAcute' => '´',
+ 'DiacriticalDot' => '˙',
+ 'DiacriticalDoubleAcute' => '˝',
+ 'DiacriticalGrave' => '`',
+ 'DiacriticalTilde' => '˜',
+ 'diam' => '⋄',
+ 'Diamond' => '⋄',
+ 'diamond' => '⋄',
+ 'diamondsuit' => '♦',
+ 'diams' => '♦',
+ 'die' => '¨',
+ 'DifferentialD' => 'ⅆ',
+ 'digamma' => 'ϝ',
+ 'disin' => '⋲',
+ 'div' => '÷',
+ 'divide' => '÷',
+ 'divid' => '÷',
+ 'divideontimes' => '⋇',
+ 'divonx' => '⋇',
+ 'DJcy' => 'Ђ',
+ 'djcy' => 'ђ',
+ 'dlcorn' => '⌞',
+ 'dlcrop' => '⌍',
+ 'dollar' => '$',
+ 'Dopf' => '𝔻',
+ 'dopf' => '𝕕',
+ 'Dot' => '¨',
+ 'dot' => '˙',
+ 'DotDot' => '⃜',
+ 'doteq' => '≐',
+ 'doteqdot' => '≑',
+ 'DotEqual' => '≐',
+ 'dotminus' => '∸',
+ 'dotplus' => '∔',
+ 'dotsquare' => '⊡',
+ 'doublebarwedge' => '⌆',
+ 'DoubleContourIntegral' => '∯',
+ 'DoubleDot' => '¨',
+ 'DoubleDownArrow' => '⇓',
+ 'DoubleLeftArrow' => '⇐',
+ 'DoubleLeftRightArrow' => '⇔',
+ 'DoubleLeftTee' => '⫤',
+ 'DoubleLongLeftArrow' => '⟸',
+ 'DoubleLongLeftRightArrow' => '⟺',
+ 'DoubleLongRightArrow' => '⟹',
+ 'DoubleRightArrow' => '⇒',
+ 'DoubleRightTee' => '⊨',
+ 'DoubleUpArrow' => '⇑',
+ 'DoubleUpDownArrow' => '⇕',
+ 'DoubleVerticalBar' => '∥',
+ 'DownArrow' => '↓',
+ 'Downarrow' => '⇓',
+ 'downarrow' => '↓',
+ 'DownArrowBar' => '⤓',
+ 'DownArrowUpArrow' => '⇵',
+ 'DownBreve' => '̑',
+ 'downdownarrows' => '⇊',
+ 'downharpoonleft' => '⇃',
+ 'downharpoonright' => '⇂',
+ 'DownLeftRightVector' => '⥐',
+ 'DownLeftTeeVector' => '⥞',
+ 'DownLeftVector' => '↽',
+ 'DownLeftVectorBar' => '⥖',
+ 'DownRightTeeVector' => '⥟',
+ 'DownRightVector' => '⇁',
+ 'DownRightVectorBar' => '⥗',
+ 'DownTee' => '⊤',
+ 'DownTeeArrow' => '↧',
+ 'drbkarow' => '⤐',
+ 'drcorn' => '⌟',
+ 'drcrop' => '⌌',
+ 'Dscr' => '𝒟',
+ 'dscr' => '𝒹',
+ 'DScy' => 'Ѕ',
+ 'dscy' => 'ѕ',
+ 'dsol' => '⧶',
+ 'Dstrok' => 'Đ',
+ 'dstrok' => 'đ',
+ 'dtdot' => '⋱',
+ 'dtri' => '▿',
+ 'dtrif' => '▾',
+ 'duarr' => '⇵',
+ 'duhar' => '⥯',
+ 'dwangle' => '⦦',
+ 'DZcy' => 'Џ',
+ 'dzcy' => 'џ',
+ 'dzigrarr' => '⟿',
+ 'Eacute' => 'É',
+ 'Eacut' => 'É',
+ 'eacute' => 'é',
+ 'eacut' => 'é',
+ 'easter' => '⩮',
+ 'Ecaron' => 'Ě',
+ 'ecaron' => 'ě',
+ 'ecir' => 'ê',
+ 'Ecirc' => 'Ê',
+ 'Ecir' => 'Ê',
+ 'ecirc' => 'ê',
+ 'ecolon' => '≕',
+ 'Ecy' => 'Э',
+ 'ecy' => 'э',
+ 'eDDot' => '⩷',
+ 'Edot' => 'Ė',
+ 'eDot' => '≑',
+ 'edot' => 'ė',
+ 'ee' => 'ⅇ',
+ 'efDot' => '≒',
+ 'Efr' => '𝔈',
+ 'efr' => '𝔢',
+ 'eg' => '⪚',
+ 'Egrave' => 'È',
+ 'Egrav' => 'È',
+ 'egrave' => 'è',
+ 'egrav' => 'è',
+ 'egs' => '⪖',
+ 'egsdot' => '⪘',
+ 'el' => '⪙',
+ 'Element' => '∈',
+ 'elinters' => '⏧',
+ 'ell' => 'ℓ',
+ 'els' => '⪕',
+ 'elsdot' => '⪗',
+ 'Emacr' => 'Ē',
+ 'emacr' => 'ē',
+ 'empty' => '∅',
+ 'emptyset' => '∅',
+ 'EmptySmallSquare' => '◻',
+ 'emptyv' => '∅',
+ 'EmptyVerySmallSquare' => '▫',
+ 'emsp' => ' ',
+ 'emsp13' => ' ',
+ 'emsp14' => ' ',
+ 'ENG' => 'Ŋ',
+ 'eng' => 'ŋ',
+ 'ensp' => ' ',
+ 'Eogon' => 'Ę',
+ 'eogon' => 'ę',
+ 'Eopf' => '𝔼',
+ 'eopf' => '𝕖',
+ 'epar' => '⋕',
+ 'eparsl' => '⧣',
+ 'eplus' => '⩱',
+ 'epsi' => 'ε',
+ 'Epsilon' => 'Ε',
+ 'epsilon' => 'ε',
+ 'epsiv' => 'ϵ',
+ 'eqcirc' => '≖',
+ 'eqcolon' => '≕',
+ 'eqsim' => '≂',
+ 'eqslantgtr' => '⪖',
+ 'eqslantless' => '⪕',
+ 'Equal' => '⩵',
+ 'equals' => '=',
+ 'EqualTilde' => '≂',
+ 'equest' => '≟',
+ 'Equilibrium' => '⇌',
+ 'equiv' => '≡',
+ 'equivDD' => '⩸',
+ 'eqvparsl' => '⧥',
+ 'erarr' => '⥱',
+ 'erDot' => '≓',
+ 'Escr' => 'ℰ',
+ 'escr' => 'ℯ',
+ 'esdot' => '≐',
+ 'Esim' => '⩳',
+ 'esim' => '≂',
+ 'Eta' => 'Η',
+ 'eta' => 'η',
+ 'ETH' => 'Ð',
+ 'ET' => 'Ð',
+ 'eth' => 'ð',
+ 'et' => 'ð',
+ 'Euml' => 'Ë',
+ 'Eum' => 'Ë',
+ 'euml' => 'ë',
+ 'eum' => 'ë',
+ 'euro' => '€',
+ 'excl' => '!',
+ 'exist' => '∃',
+ 'Exists' => '∃',
+ 'expectation' => 'ℰ',
+ 'ExponentialE' => 'ⅇ',
+ 'exponentiale' => 'ⅇ',
+ 'fallingdotseq' => '≒',
+ 'Fcy' => 'Ф',
+ 'fcy' => 'ф',
+ 'female' => '♀',
+ 'ffilig' => 'ffi',
+ 'fflig' => 'ff',
+ 'ffllig' => 'ffl',
+ 'Ffr' => '𝔉',
+ 'ffr' => '𝔣',
+ 'filig' => 'fi',
+ 'FilledSmallSquare' => '◼',
+ 'FilledVerySmallSquare' => '▪',
+ 'fjlig' => 'fj',
+ 'flat' => '♭',
+ 'fllig' => 'fl',
+ 'fltns' => '▱',
+ 'fnof' => 'ƒ',
+ 'Fopf' => '𝔽',
+ 'fopf' => '𝕗',
+ 'ForAll' => '∀',
+ 'forall' => '∀',
+ 'fork' => '⋔',
+ 'forkv' => '⫙',
+ 'Fouriertrf' => 'ℱ',
+ 'fpartint' => '⨍',
+ 'frac12' => '½',
+ 'frac1' => '¼',
+ 'frac13' => '⅓',
+ 'frac14' => '¼',
+ 'frac15' => '⅕',
+ 'frac16' => '⅙',
+ 'frac18' => '⅛',
+ 'frac23' => '⅔',
+ 'frac25' => '⅖',
+ 'frac34' => '¾',
+ 'frac3' => '¾',
+ 'frac35' => '⅗',
+ 'frac38' => '⅜',
+ 'frac45' => '⅘',
+ 'frac56' => '⅚',
+ 'frac58' => '⅝',
+ 'frac78' => '⅞',
+ 'frasl' => '⁄',
+ 'frown' => '⌢',
+ 'Fscr' => 'ℱ',
+ 'fscr' => '𝒻',
+ 'gacute' => 'ǵ',
+ 'Gamma' => 'Γ',
+ 'gamma' => 'γ',
+ 'Gammad' => 'Ϝ',
+ 'gammad' => 'ϝ',
+ 'gap' => '⪆',
+ 'Gbreve' => 'Ğ',
+ 'gbreve' => 'ğ',
+ 'Gcedil' => 'Ģ',
+ 'Gcirc' => 'Ĝ',
+ 'gcirc' => 'ĝ',
+ 'Gcy' => 'Г',
+ 'gcy' => 'г',
+ 'Gdot' => 'Ġ',
+ 'gdot' => 'ġ',
+ 'gE' => '≧',
+ 'ge' => '≥',
+ 'gEl' => '⪌',
+ 'gel' => '⋛',
+ 'geq' => '≥',
+ 'geqq' => '≧',
+ 'geqslant' => '⩾',
+ 'ges' => '⩾',
+ 'gescc' => '⪩',
+ 'gesdot' => '⪀',
+ 'gesdoto' => '⪂',
+ 'gesdotol' => '⪄',
+ 'gesl' => '⋛︀',
+ 'gesles' => '⪔',
+ 'Gfr' => '𝔊',
+ 'gfr' => '𝔤',
+ 'Gg' => '⋙',
+ 'gg' => '≫',
+ 'ggg' => '⋙',
+ 'gimel' => 'ℷ',
+ 'GJcy' => 'Ѓ',
+ 'gjcy' => 'ѓ',
+ 'gl' => '≷',
+ 'gla' => '⪥',
+ 'glE' => '⪒',
+ 'glj' => '⪤',
+ 'gnap' => '⪊',
+ 'gnapprox' => '⪊',
+ 'gnE' => '≩',
+ 'gne' => '⪈',
+ 'gneq' => '⪈',
+ 'gneqq' => '≩',
+ 'gnsim' => '⋧',
+ 'Gopf' => '𝔾',
+ 'gopf' => '𝕘',
+ 'grave' => '`',
+ 'GreaterEqual' => '≥',
+ 'GreaterEqualLess' => '⋛',
+ 'GreaterFullEqual' => '≧',
+ 'GreaterGreater' => '⪢',
+ 'GreaterLess' => '≷',
+ 'GreaterSlantEqual' => '⩾',
+ 'GreaterTilde' => '≳',
+ 'Gscr' => '𝒢',
+ 'gscr' => 'ℊ',
+ 'gsim' => '≳',
+ 'gsime' => '⪎',
+ 'gsiml' => '⪐',
+ 'GT' => '>',
+ 'G' => '>',
+ 'Gt' => '≫',
+ 'gt' => '>',
+ 'g' => '>',
+ 'gtcc' => '⪧',
+ 'gtcir' => '⩺',
+ 'gtdot' => '⋗',
+ 'gtlPar' => '⦕',
+ 'gtquest' => '⩼',
+ 'gtrapprox' => '⪆',
+ 'gtrarr' => '⥸',
+ 'gtrdot' => '⋗',
+ 'gtreqless' => '⋛',
+ 'gtreqqless' => '⪌',
+ 'gtrless' => '≷',
+ 'gtrsim' => '≳',
+ 'gvertneqq' => '≩︀',
+ 'gvnE' => '≩︀',
+ 'Hacek' => 'ˇ',
+ 'hairsp' => ' ',
+ 'half' => '½',
+ 'hamilt' => 'ℋ',
+ 'HARDcy' => 'Ъ',
+ 'hardcy' => 'ъ',
+ 'hArr' => '⇔',
+ 'harr' => '↔',
+ 'harrcir' => '⥈',
+ 'harrw' => '↭',
+ 'Hat' => '^',
+ 'hbar' => 'ℏ',
+ 'Hcirc' => 'Ĥ',
+ 'hcirc' => 'ĥ',
+ 'hearts' => '♥',
+ 'heartsuit' => '♥',
+ 'hellip' => '…',
+ 'hercon' => '⊹',
+ 'Hfr' => 'ℌ',
+ 'hfr' => '𝔥',
+ 'HilbertSpace' => 'ℋ',
+ 'hksearow' => '⤥',
+ 'hkswarow' => '⤦',
+ 'hoarr' => '⇿',
+ 'homtht' => '∻',
+ 'hookleftarrow' => '↩',
+ 'hookrightarrow' => '↪',
+ 'Hopf' => 'ℍ',
+ 'hopf' => '𝕙',
+ 'horbar' => '―',
+ 'HorizontalLine' => '─',
+ 'Hscr' => 'ℋ',
+ 'hscr' => '𝒽',
+ 'hslash' => 'ℏ',
+ 'Hstrok' => 'Ħ',
+ 'hstrok' => 'ħ',
+ 'HumpDownHump' => '≎',
+ 'HumpEqual' => '≏',
+ 'hybull' => '⁃',
+ 'hyphen' => '‐',
+ 'Iacute' => 'Í',
+ 'Iacut' => 'Í',
+ 'iacute' => 'í',
+ 'iacut' => 'í',
+ 'ic' => '',
+ 'Icirc' => 'Î',
+ 'Icir' => 'Î',
+ 'icirc' => 'î',
+ 'icir' => 'î',
+ 'Icy' => 'И',
+ 'icy' => 'и',
+ 'Idot' => 'İ',
+ 'IEcy' => 'Е',
+ 'iecy' => 'е',
+ 'iexcl' => '¡',
+ 'iexc' => '¡',
+ 'iff' => '⇔',
+ 'Ifr' => 'ℑ',
+ 'ifr' => '𝔦',
+ 'Igrave' => 'Ì',
+ 'Igrav' => 'Ì',
+ 'igrave' => 'ì',
+ 'igrav' => 'ì',
+ 'ii' => 'ⅈ',
+ 'iiiint' => '⨌',
+ 'iiint' => '∭',
+ 'iinfin' => '⧜',
+ 'iiota' => '℩',
+ 'IJlig' => 'IJ',
+ 'ijlig' => 'ij',
+ 'Im' => 'ℑ',
+ 'Imacr' => 'Ī',
+ 'imacr' => 'ī',
+ 'image' => 'ℑ',
+ 'ImaginaryI' => 'ⅈ',
+ 'imagline' => 'ℐ',
+ 'imagpart' => 'ℑ',
+ 'imath' => 'ı',
+ 'imof' => '⊷',
+ 'imped' => 'Ƶ',
+ 'Implies' => '⇒',
+ 'in' => '∈',
+ 'incare' => '℅',
+ 'infin' => '∞',
+ 'infintie' => '⧝',
+ 'inodot' => 'ı',
+ 'Int' => '∬',
+ 'int' => '∫',
+ 'intcal' => '⊺',
+ 'integers' => 'ℤ',
+ 'Integral' => '∫',
+ 'intercal' => '⊺',
+ 'Intersection' => '⋂',
+ 'intlarhk' => '⨗',
+ 'intprod' => '⨼',
+ 'InvisibleComma' => '',
+ 'InvisibleTimes' => '',
+ 'IOcy' => 'Ё',
+ 'iocy' => 'ё',
+ 'Iogon' => 'Į',
+ 'iogon' => 'į',
+ 'Iopf' => '𝕀',
+ 'iopf' => '𝕚',
+ 'Iota' => 'Ι',
+ 'iota' => 'ι',
+ 'iprod' => '⨼',
+ 'iquest' => '¿',
+ 'iques' => '¿',
+ 'Iscr' => 'ℐ',
+ 'iscr' => '𝒾',
+ 'isin' => '∈',
+ 'isindot' => '⋵',
+ 'isinE' => '⋹',
+ 'isins' => '⋴',
+ 'isinsv' => '⋳',
+ 'isinv' => '∈',
+ 'it' => '',
+ 'Itilde' => 'Ĩ',
+ 'itilde' => 'ĩ',
+ 'Iukcy' => 'І',
+ 'iukcy' => 'і',
+ 'Iuml' => 'Ï',
+ 'Ium' => 'Ï',
+ 'iuml' => 'ï',
+ 'ium' => 'ï',
+ 'Jcirc' => 'Ĵ',
+ 'jcirc' => 'ĵ',
+ 'Jcy' => 'Й',
+ 'jcy' => 'й',
+ 'Jfr' => '𝔍',
+ 'jfr' => '𝔧',
+ 'jmath' => 'ȷ',
+ 'Jopf' => '𝕁',
+ 'jopf' => '𝕛',
+ 'Jscr' => '𝒥',
+ 'jscr' => '𝒿',
+ 'Jsercy' => 'Ј',
+ 'jsercy' => 'ј',
+ 'Jukcy' => 'Є',
+ 'jukcy' => 'є',
+ 'Kappa' => 'Κ',
+ 'kappa' => 'κ',
+ 'kappav' => 'ϰ',
+ 'Kcedil' => 'Ķ',
+ 'kcedil' => 'ķ',
+ 'Kcy' => 'К',
+ 'kcy' => 'к',
+ 'Kfr' => '𝔎',
+ 'kfr' => '𝔨',
+ 'kgreen' => 'ĸ',
+ 'KHcy' => 'Х',
+ 'khcy' => 'х',
+ 'KJcy' => 'Ќ',
+ 'kjcy' => 'ќ',
+ 'Kopf' => '𝕂',
+ 'kopf' => '𝕜',
+ 'Kscr' => '𝒦',
+ 'kscr' => '𝓀',
+ 'lAarr' => '⇚',
+ 'Lacute' => 'Ĺ',
+ 'lacute' => 'ĺ',
+ 'laemptyv' => '⦴',
+ 'lagran' => 'ℒ',
+ 'Lambda' => 'Λ',
+ 'lambda' => 'λ',
+ 'Lang' => '⟪',
+ 'lang' => '⟨',
+ 'langd' => '⦑',
+ 'langle' => '⟨',
+ 'lap' => '⪅',
+ 'Laplacetrf' => 'ℒ',
+ 'laquo' => '«',
+ 'laqu' => '«',
+ 'Larr' => '↞',
+ 'lArr' => '⇐',
+ 'larr' => '←',
+ 'larrb' => '⇤',
+ 'larrbfs' => '⤟',
+ 'larrfs' => '⤝',
+ 'larrhk' => '↩',
+ 'larrlp' => '↫',
+ 'larrpl' => '⤹',
+ 'larrsim' => '⥳',
+ 'larrtl' => '↢',
+ 'lat' => '⪫',
+ 'lAtail' => '⤛',
+ 'latail' => '⤙',
+ 'late' => '⪭',
+ 'lates' => '⪭︀',
+ 'lBarr' => '⤎',
+ 'lbarr' => '⤌',
+ 'lbbrk' => '❲',
+ 'lbrace' => '{',
+ 'lbrack' => '[',
+ 'lbrke' => '⦋',
+ 'lbrksld' => '⦏',
+ 'lbrkslu' => '⦍',
+ 'Lcaron' => 'Ľ',
+ 'lcaron' => 'ľ',
+ 'Lcedil' => 'Ļ',
+ 'lcedil' => 'ļ',
+ 'lceil' => '⌈',
+ 'lcub' => '{',
+ 'Lcy' => 'Л',
+ 'lcy' => 'л',
+ 'ldca' => '⤶',
+ 'ldquo' => '“',
+ 'ldquor' => '„',
+ 'ldrdhar' => '⥧',
+ 'ldrushar' => '⥋',
+ 'ldsh' => '↲',
+ 'lE' => '≦',
+ 'le' => '≤',
+ 'LeftAngleBracket' => '⟨',
+ 'LeftArrow' => '←',
+ 'Leftarrow' => '⇐',
+ 'leftarrow' => '←',
+ 'LeftArrowBar' => '⇤',
+ 'LeftArrowRightArrow' => '⇆',
+ 'leftarrowtail' => '↢',
+ 'LeftCeiling' => '⌈',
+ 'LeftDoubleBracket' => '⟦',
+ 'LeftDownTeeVector' => '⥡',
+ 'LeftDownVector' => '⇃',
+ 'LeftDownVectorBar' => '⥙',
+ 'LeftFloor' => '⌊',
+ 'leftharpoondown' => '↽',
+ 'leftharpoonup' => '↼',
+ 'leftleftarrows' => '⇇',
+ 'LeftRightArrow' => '↔',
+ 'Leftrightarrow' => '⇔',
+ 'leftrightarrow' => '↔',
+ 'leftrightarrows' => '⇆',
+ 'leftrightharpoons' => '⇋',
+ 'leftrightsquigarrow' => '↭',
+ 'LeftRightVector' => '⥎',
+ 'LeftTee' => '⊣',
+ 'LeftTeeArrow' => '↤',
+ 'LeftTeeVector' => '⥚',
+ 'leftthreetimes' => '⋋',
+ 'LeftTriangle' => '⊲',
+ 'LeftTriangleBar' => '⧏',
+ 'LeftTriangleEqual' => '⊴',
+ 'LeftUpDownVector' => '⥑',
+ 'LeftUpTeeVector' => '⥠',
+ 'LeftUpVector' => '↿',
+ 'LeftUpVectorBar' => '⥘',
+ 'LeftVector' => '↼',
+ 'LeftVectorBar' => '⥒',
+ 'lEg' => '⪋',
+ 'leg' => '⋚',
+ 'leq' => '≤',
+ 'leqq' => '≦',
+ 'leqslant' => '⩽',
+ 'les' => '⩽',
+ 'lescc' => '⪨',
+ 'lesdot' => '⩿',
+ 'lesdoto' => '⪁',
+ 'lesdotor' => '⪃',
+ 'lesg' => '⋚︀',
+ 'lesges' => '⪓',
+ 'lessapprox' => '⪅',
+ 'lessdot' => '⋖',
+ 'lesseqgtr' => '⋚',
+ 'lesseqqgtr' => '⪋',
+ 'LessEqualGreater' => '⋚',
+ 'LessFullEqual' => '≦',
+ 'LessGreater' => '≶',
+ 'lessgtr' => '≶',
+ 'LessLess' => '⪡',
+ 'lesssim' => '≲',
+ 'LessSlantEqual' => '⩽',
+ 'LessTilde' => '≲',
+ 'lfisht' => '⥼',
+ 'lfloor' => '⌊',
+ 'Lfr' => '𝔏',
+ 'lfr' => '𝔩',
+ 'lg' => '≶',
+ 'lgE' => '⪑',
+ 'lHar' => '⥢',
+ 'lhard' => '↽',
+ 'lharu' => '↼',
+ 'lharul' => '⥪',
+ 'lhblk' => '▄',
+ 'LJcy' => 'Љ',
+ 'ljcy' => 'љ',
+ 'Ll' => '⋘',
+ 'll' => '≪',
+ 'llarr' => '⇇',
+ 'llcorner' => '⌞',
+ 'Lleftarrow' => '⇚',
+ 'llhard' => '⥫',
+ 'lltri' => '◺',
+ 'Lmidot' => 'Ŀ',
+ 'lmidot' => 'ŀ',
+ 'lmoust' => '⎰',
+ 'lmoustache' => '⎰',
+ 'lnap' => '⪉',
+ 'lnapprox' => '⪉',
+ 'lnE' => '≨',
+ 'lne' => '⪇',
+ 'lneq' => '⪇',
+ 'lneqq' => '≨',
+ 'lnsim' => '⋦',
+ 'loang' => '⟬',
+ 'loarr' => '⇽',
+ 'lobrk' => '⟦',
+ 'LongLeftArrow' => '⟵',
+ 'Longleftarrow' => '⟸',
+ 'longleftarrow' => '⟵',
+ 'LongLeftRightArrow' => '⟷',
+ 'Longleftrightarrow' => '⟺',
+ 'longleftrightarrow' => '⟷',
+ 'longmapsto' => '⟼',
+ 'LongRightArrow' => '⟶',
+ 'Longrightarrow' => '⟹',
+ 'longrightarrow' => '⟶',
+ 'looparrowleft' => '↫',
+ 'looparrowright' => '↬',
+ 'lopar' => '⦅',
+ 'Lopf' => '𝕃',
+ 'lopf' => '𝕝',
+ 'loplus' => '⨭',
+ 'lotimes' => '⨴',
+ 'lowast' => '∗',
+ 'lowbar' => '_',
+ 'LowerLeftArrow' => '↙',
+ 'LowerRightArrow' => '↘',
+ 'loz' => '◊',
+ 'lozenge' => '◊',
+ 'lozf' => '⧫',
+ 'lpar' => '(',
+ 'lparlt' => '⦓',
+ 'lrarr' => '⇆',
+ 'lrcorner' => '⌟',
+ 'lrhar' => '⇋',
+ 'lrhard' => '⥭',
+ 'lrm' => '',
+ 'lrtri' => '⊿',
+ 'lsaquo' => '‹',
+ 'Lscr' => 'ℒ',
+ 'lscr' => '𝓁',
+ 'Lsh' => '↰',
+ 'lsh' => '↰',
+ 'lsim' => '≲',
+ 'lsime' => '⪍',
+ 'lsimg' => '⪏',
+ 'lsqb' => '[',
+ 'lsquo' => '‘',
+ 'lsquor' => '‚',
+ 'Lstrok' => 'Ł',
+ 'lstrok' => 'ł',
+ 'LT' => '<',
+ 'L' => '<',
+ 'Lt' => '≪',
+ 'lt' => '<',
+ 'l' => '<',
+ 'ltcc' => '⪦',
+ 'ltcir' => '⩹',
+ 'ltdot' => '⋖',
+ 'lthree' => '⋋',
+ 'ltimes' => '⋉',
+ 'ltlarr' => '⥶',
+ 'ltquest' => '⩻',
+ 'ltri' => '◃',
+ 'ltrie' => '⊴',
+ 'ltrif' => '◂',
+ 'ltrPar' => '⦖',
+ 'lurdshar' => '⥊',
+ 'luruhar' => '⥦',
+ 'lvertneqq' => '≨︀',
+ 'lvnE' => '≨︀',
+ 'macr' => '¯',
+ 'mac' => '¯',
+ 'male' => '♂',
+ 'malt' => '✠',
+ 'maltese' => '✠',
+ 'Map' => '⤅',
+ 'map' => '↦',
+ 'mapsto' => '↦',
+ 'mapstodown' => '↧',
+ 'mapstoleft' => '↤',
+ 'mapstoup' => '↥',
+ 'marker' => '▮',
+ 'mcomma' => '⨩',
+ 'Mcy' => 'М',
+ 'mcy' => 'м',
+ 'mdash' => '—',
+ 'mDDot' => '∺',
+ 'measuredangle' => '∡',
+ 'MediumSpace' => ' ',
+ 'Mellintrf' => 'ℳ',
+ 'Mfr' => '𝔐',
+ 'mfr' => '𝔪',
+ 'mho' => '℧',
+ 'micro' => 'µ',
+ 'micr' => 'µ',
+ 'mid' => '∣',
+ 'midast' => '*',
+ 'midcir' => '⫰',
+ 'middot' => '·',
+ 'middo' => '·',
+ 'minus' => '−',
+ 'minusb' => '⊟',
+ 'minusd' => '∸',
+ 'minusdu' => '⨪',
+ 'MinusPlus' => '∓',
+ 'mlcp' => '⫛',
+ 'mldr' => '…',
+ 'mnplus' => '∓',
+ 'models' => '⊧',
+ 'Mopf' => '𝕄',
+ 'mopf' => '𝕞',
+ 'mp' => '∓',
+ 'Mscr' => 'ℳ',
+ 'mscr' => '𝓂',
+ 'mstpos' => '∾',
+ 'Mu' => 'Μ',
+ 'mu' => 'μ',
+ 'multimap' => '⊸',
+ 'mumap' => '⊸',
+ 'nabla' => '∇',
+ 'Nacute' => 'Ń',
+ 'nacute' => 'ń',
+ 'nang' => '∠⃒',
+ 'nap' => '≉',
+ 'napE' => '⩰̸',
+ 'napid' => '≋̸',
+ 'napos' => 'ʼn',
+ 'napprox' => '≉',
+ 'natur' => '♮',
+ 'natural' => '♮',
+ 'naturals' => 'ℕ',
+ 'nbsp' => ' ',
+ 'nbs' => ' ',
+ 'nbump' => '≎̸',
+ 'nbumpe' => '≏̸',
+ 'ncap' => '⩃',
+ 'Ncaron' => 'Ň',
+ 'ncaron' => 'ň',
+ 'Ncedil' => 'Ņ',
+ 'ncedil' => 'ņ',
+ 'ncong' => '≇',
+ 'ncongdot' => '⩭̸',
+ 'ncup' => '⩂',
+ 'Ncy' => 'Н',
+ 'ncy' => 'н',
+ 'ndash' => '–',
+ 'ne' => '≠',
+ 'nearhk' => '⤤',
+ 'neArr' => '⇗',
+ 'nearr' => '↗',
+ 'nearrow' => '↗',
+ 'nedot' => '≐̸',
+ 'NegativeMediumSpace' => '',
+ 'NegativeThickSpace' => '',
+ 'NegativeThinSpace' => '',
+ 'NegativeVeryThinSpace' => '',
+ 'nequiv' => '≢',
+ 'nesear' => '⤨',
+ 'nesim' => '≂̸',
+ 'NestedGreaterGreater' => '≫',
+ 'NestedLessLess' => '≪',
+ 'NewLine' => '
+',
+ 'nexist' => '∄',
+ 'nexists' => '∄',
+ 'Nfr' => '𝔑',
+ 'nfr' => '𝔫',
+ 'ngE' => '≧̸',
+ 'nge' => '≱',
+ 'ngeq' => '≱',
+ 'ngeqq' => '≧̸',
+ 'ngeqslant' => '⩾̸',
+ 'nges' => '⩾̸',
+ 'nGg' => '⋙̸',
+ 'ngsim' => '≵',
+ 'nGt' => '≫⃒',
+ 'ngt' => '≯',
+ 'ngtr' => '≯',
+ 'nGtv' => '≫̸',
+ 'nhArr' => '⇎',
+ 'nharr' => '↮',
+ 'nhpar' => '⫲',
+ 'ni' => '∋',
+ 'nis' => '⋼',
+ 'nisd' => '⋺',
+ 'niv' => '∋',
+ 'NJcy' => 'Њ',
+ 'njcy' => 'њ',
+ 'nlArr' => '⇍',
+ 'nlarr' => '↚',
+ 'nldr' => '‥',
+ 'nlE' => '≦̸',
+ 'nle' => '≰',
+ 'nLeftarrow' => '⇍',
+ 'nleftarrow' => '↚',
+ 'nLeftrightarrow' => '⇎',
+ 'nleftrightarrow' => '↮',
+ 'nleq' => '≰',
+ 'nleqq' => '≦̸',
+ 'nleqslant' => '⩽̸',
+ 'nles' => '⩽̸',
+ 'nless' => '≮',
+ 'nLl' => '⋘̸',
+ 'nlsim' => '≴',
+ 'nLt' => '≪⃒',
+ 'nlt' => '≮',
+ 'nltri' => '⋪',
+ 'nltrie' => '⋬',
+ 'nLtv' => '≪̸',
+ 'nmid' => '∤',
+ 'NoBreak' => '',
+ 'NonBreakingSpace' => ' ',
+ 'Nopf' => 'ℕ',
+ 'nopf' => '𝕟',
+ 'Not' => '⫬',
+ 'not' => '¬',
+ 'no' => '¬',
+ 'NotCongruent' => '≢',
+ 'NotCupCap' => '≭',
+ 'NotDoubleVerticalBar' => '∦',
+ 'NotElement' => '∉',
+ 'NotEqual' => '≠',
+ 'NotEqualTilde' => '≂̸',
+ 'NotExists' => '∄',
+ 'NotGreater' => '≯',
+ 'NotGreaterEqual' => '≱',
+ 'NotGreaterFullEqual' => '≧̸',
+ 'NotGreaterGreater' => '≫̸',
+ 'NotGreaterLess' => '≹',
+ 'NotGreaterSlantEqual' => '⩾̸',
+ 'NotGreaterTilde' => '≵',
+ 'NotHumpDownHump' => '≎̸',
+ 'NotHumpEqual' => '≏̸',
+ 'notin' => '∉',
+ 'notindot' => '⋵̸',
+ 'notinE' => '⋹̸',
+ 'notinva' => '∉',
+ 'notinvb' => '⋷',
+ 'notinvc' => '⋶',
+ 'NotLeftTriangle' => '⋪',
+ 'NotLeftTriangleBar' => '⧏̸',
+ 'NotLeftTriangleEqual' => '⋬',
+ 'NotLess' => '≮',
+ 'NotLessEqual' => '≰',
+ 'NotLessGreater' => '≸',
+ 'NotLessLess' => '≪̸',
+ 'NotLessSlantEqual' => '⩽̸',
+ 'NotLessTilde' => '≴',
+ 'NotNestedGreaterGreater' => '⪢̸',
+ 'NotNestedLessLess' => '⪡̸',
+ 'notni' => '∌',
+ 'notniva' => '∌',
+ 'notnivb' => '⋾',
+ 'notnivc' => '⋽',
+ 'NotPrecedes' => '⊀',
+ 'NotPrecedesEqual' => '⪯̸',
+ 'NotPrecedesSlantEqual' => '⋠',
+ 'NotReverseElement' => '∌',
+ 'NotRightTriangle' => '⋫',
+ 'NotRightTriangleBar' => '⧐̸',
+ 'NotRightTriangleEqual' => '⋭',
+ 'NotSquareSubset' => '⊏̸',
+ 'NotSquareSubsetEqual' => '⋢',
+ 'NotSquareSuperset' => '⊐̸',
+ 'NotSquareSupersetEqual' => '⋣',
+ 'NotSubset' => '⊂⃒',
+ 'NotSubsetEqual' => '⊈',
+ 'NotSucceeds' => '⊁',
+ 'NotSucceedsEqual' => '⪰̸',
+ 'NotSucceedsSlantEqual' => '⋡',
+ 'NotSucceedsTilde' => '≿̸',
+ 'NotSuperset' => '⊃⃒',
+ 'NotSupersetEqual' => '⊉',
+ 'NotTilde' => '≁',
+ 'NotTildeEqual' => '≄',
+ 'NotTildeFullEqual' => '≇',
+ 'NotTildeTilde' => '≉',
+ 'NotVerticalBar' => '∤',
+ 'npar' => '∦',
+ 'nparallel' => '∦',
+ 'nparsl' => '⫽⃥',
+ 'npart' => '∂̸',
+ 'npolint' => '⨔',
+ 'npr' => '⊀',
+ 'nprcue' => '⋠',
+ 'npre' => '⪯̸',
+ 'nprec' => '⊀',
+ 'npreceq' => '⪯̸',
+ 'nrArr' => '⇏',
+ 'nrarr' => '↛',
+ 'nrarrc' => '⤳̸',
+ 'nrarrw' => '↝̸',
+ 'nRightarrow' => '⇏',
+ 'nrightarrow' => '↛',
+ 'nrtri' => '⋫',
+ 'nrtrie' => '⋭',
+ 'nsc' => '⊁',
+ 'nsccue' => '⋡',
+ 'nsce' => '⪰̸',
+ 'Nscr' => '𝒩',
+ 'nscr' => '𝓃',
+ 'nshortmid' => '∤',
+ 'nshortparallel' => '∦',
+ 'nsim' => '≁',
+ 'nsime' => '≄',
+ 'nsimeq' => '≄',
+ 'nsmid' => '∤',
+ 'nspar' => '∦',
+ 'nsqsube' => '⋢',
+ 'nsqsupe' => '⋣',
+ 'nsub' => '⊄',
+ 'nsubE' => '⫅̸',
+ 'nsube' => '⊈',
+ 'nsubset' => '⊂⃒',
+ 'nsubseteq' => '⊈',
+ 'nsubseteqq' => '⫅̸',
+ 'nsucc' => '⊁',
+ 'nsucceq' => '⪰̸',
+ 'nsup' => '⊅',
+ 'nsupE' => '⫆̸',
+ 'nsupe' => '⊉',
+ 'nsupset' => '⊃⃒',
+ 'nsupseteq' => '⊉',
+ 'nsupseteqq' => '⫆̸',
+ 'ntgl' => '≹',
+ 'Ntilde' => 'Ñ',
+ 'Ntild' => 'Ñ',
+ 'ntilde' => 'ñ',
+ 'ntild' => 'ñ',
+ 'ntlg' => '≸',
+ 'ntriangleleft' => '⋪',
+ 'ntrianglelefteq' => '⋬',
+ 'ntriangleright' => '⋫',
+ 'ntrianglerighteq' => '⋭',
+ 'Nu' => 'Ν',
+ 'nu' => 'ν',
+ 'num' => '#',
+ 'numero' => '№',
+ 'numsp' => ' ',
+ 'nvap' => '≍⃒',
+ 'nVDash' => '⊯',
+ 'nVdash' => '⊮',
+ 'nvDash' => '⊭',
+ 'nvdash' => '⊬',
+ 'nvge' => '≥⃒',
+ 'nvgt' => '>⃒',
+ 'nvHarr' => '⤄',
+ 'nvinfin' => '⧞',
+ 'nvlArr' => '⤂',
+ 'nvle' => '≤⃒',
+ 'nvlt' => '<⃒',
+ 'nvltrie' => '⊴⃒',
+ 'nvrArr' => '⤃',
+ 'nvrtrie' => '⊵⃒',
+ 'nvsim' => '∼⃒',
+ 'nwarhk' => '⤣',
+ 'nwArr' => '⇖',
+ 'nwarr' => '↖',
+ 'nwarrow' => '↖',
+ 'nwnear' => '⤧',
+ 'Oacute' => 'Ó',
+ 'Oacut' => 'Ó',
+ 'oacute' => 'ó',
+ 'oacut' => 'ó',
+ 'oast' => '⊛',
+ 'ocir' => 'ô',
+ 'Ocirc' => 'Ô',
+ 'Ocir' => 'Ô',
+ 'ocirc' => 'ô',
+ 'Ocy' => 'О',
+ 'ocy' => 'о',
+ 'odash' => '⊝',
+ 'Odblac' => 'Ő',
+ 'odblac' => 'ő',
+ 'odiv' => '⨸',
+ 'odot' => '⊙',
+ 'odsold' => '⦼',
+ 'OElig' => 'Œ',
+ 'oelig' => 'œ',
+ 'ofcir' => '⦿',
+ 'Ofr' => '𝔒',
+ 'ofr' => '𝔬',
+ 'ogon' => '˛',
+ 'Ograve' => 'Ò',
+ 'Ograv' => 'Ò',
+ 'ograve' => 'ò',
+ 'ograv' => 'ò',
+ 'ogt' => '⧁',
+ 'ohbar' => '⦵',
+ 'ohm' => 'Ω',
+ 'oint' => '∮',
+ 'olarr' => '↺',
+ 'olcir' => '⦾',
+ 'olcross' => '⦻',
+ 'oline' => '‾',
+ 'olt' => '⧀',
+ 'Omacr' => 'Ō',
+ 'omacr' => 'ō',
+ 'Omega' => 'Ω',
+ 'omega' => 'ω',
+ 'Omicron' => 'Ο',
+ 'omicron' => 'ο',
+ 'omid' => '⦶',
+ 'ominus' => '⊖',
+ 'Oopf' => '𝕆',
+ 'oopf' => '𝕠',
+ 'opar' => '⦷',
+ 'OpenCurlyDoubleQuote' => '“',
+ 'OpenCurlyQuote' => '‘',
+ 'operp' => '⦹',
+ 'oplus' => '⊕',
+ 'Or' => '⩔',
+ 'or' => '∨',
+ 'orarr' => '↻',
+ 'ord' => 'º',
+ 'order' => 'ℴ',
+ 'orderof' => 'ℴ',
+ 'ordf' => 'ª',
+ 'ordm' => 'º',
+ 'origof' => '⊶',
+ 'oror' => '⩖',
+ 'orslope' => '⩗',
+ 'orv' => '⩛',
+ 'oS' => 'Ⓢ',
+ 'Oscr' => '𝒪',
+ 'oscr' => 'ℴ',
+ 'Oslash' => 'Ø',
+ 'Oslas' => 'Ø',
+ 'oslash' => 'ø',
+ 'oslas' => 'ø',
+ 'osol' => '⊘',
+ 'Otilde' => 'Õ',
+ 'Otild' => 'Õ',
+ 'otilde' => 'õ',
+ 'otild' => 'õ',
+ 'Otimes' => '⨷',
+ 'otimes' => '⊗',
+ 'otimesas' => '⨶',
+ 'Ouml' => 'Ö',
+ 'Oum' => 'Ö',
+ 'ouml' => 'ö',
+ 'oum' => 'ö',
+ 'ovbar' => '⌽',
+ 'OverBar' => '‾',
+ 'OverBrace' => '⏞',
+ 'OverBracket' => '⎴',
+ 'OverParenthesis' => '⏜',
+ 'par' => '¶',
+ 'para' => '¶',
+ 'parallel' => '∥',
+ 'parsim' => '⫳',
+ 'parsl' => '⫽',
+ 'part' => '∂',
+ 'PartialD' => '∂',
+ 'Pcy' => 'П',
+ 'pcy' => 'п',
+ 'percnt' => '%',
+ 'period' => '.',
+ 'permil' => '‰',
+ 'perp' => '⊥',
+ 'pertenk' => '‱',
+ 'Pfr' => '𝔓',
+ 'pfr' => '𝔭',
+ 'Phi' => 'Φ',
+ 'phi' => 'φ',
+ 'phiv' => 'ϕ',
+ 'phmmat' => 'ℳ',
+ 'phone' => '☎',
+ 'Pi' => 'Π',
+ 'pi' => 'π',
+ 'pitchfork' => '⋔',
+ 'piv' => 'ϖ',
+ 'planck' => 'ℏ',
+ 'planckh' => 'ℎ',
+ 'plankv' => 'ℏ',
+ 'plus' => '+',
+ 'plusacir' => '⨣',
+ 'plusb' => '⊞',
+ 'pluscir' => '⨢',
+ 'plusdo' => '∔',
+ 'plusdu' => '⨥',
+ 'pluse' => '⩲',
+ 'PlusMinus' => '±',
+ 'plusmn' => '±',
+ 'plusm' => '±',
+ 'plussim' => '⨦',
+ 'plustwo' => '⨧',
+ 'pm' => '±',
+ 'Poincareplane' => 'ℌ',
+ 'pointint' => '⨕',
+ 'Popf' => 'ℙ',
+ 'popf' => '𝕡',
+ 'pound' => '£',
+ 'poun' => '£',
+ 'Pr' => '⪻',
+ 'pr' => '≺',
+ 'prap' => '⪷',
+ 'prcue' => '≼',
+ 'prE' => '⪳',
+ 'pre' => '⪯',
+ 'prec' => '≺',
+ 'precapprox' => '⪷',
+ 'preccurlyeq' => '≼',
+ 'Precedes' => '≺',
+ 'PrecedesEqual' => '⪯',
+ 'PrecedesSlantEqual' => '≼',
+ 'PrecedesTilde' => '≾',
+ 'preceq' => '⪯',
+ 'precnapprox' => '⪹',
+ 'precneqq' => '⪵',
+ 'precnsim' => '⋨',
+ 'precsim' => '≾',
+ 'Prime' => '″',
+ 'prime' => '′',
+ 'primes' => 'ℙ',
+ 'prnap' => '⪹',
+ 'prnE' => '⪵',
+ 'prnsim' => '⋨',
+ 'prod' => '∏',
+ 'Product' => '∏',
+ 'profalar' => '⌮',
+ 'profline' => '⌒',
+ 'profsurf' => '⌓',
+ 'prop' => '∝',
+ 'Proportion' => '∷',
+ 'Proportional' => '∝',
+ 'propto' => '∝',
+ 'prsim' => '≾',
+ 'prurel' => '⊰',
+ 'Pscr' => '𝒫',
+ 'pscr' => '𝓅',
+ 'Psi' => 'Ψ',
+ 'psi' => 'ψ',
+ 'puncsp' => ' ',
+ 'Qfr' => '𝔔',
+ 'qfr' => '𝔮',
+ 'qint' => '⨌',
+ 'Qopf' => 'ℚ',
+ 'qopf' => '𝕢',
+ 'qprime' => '⁗',
+ 'Qscr' => '𝒬',
+ 'qscr' => '𝓆',
+ 'quaternions' => 'ℍ',
+ 'quatint' => '⨖',
+ 'quest' => '?',
+ 'questeq' => '≟',
+ 'QUOT' => '"',
+ 'QUO' => '"',
+ 'quot' => '"',
+ 'quo' => '"',
+ 'rAarr' => '⇛',
+ 'race' => '∽̱',
+ 'Racute' => 'Ŕ',
+ 'racute' => 'ŕ',
+ 'radic' => '√',
+ 'raemptyv' => '⦳',
+ 'Rang' => '⟫',
+ 'rang' => '⟩',
+ 'rangd' => '⦒',
+ 'range' => '⦥',
+ 'rangle' => '⟩',
+ 'raquo' => '»',
+ 'raqu' => '»',
+ 'Rarr' => '↠',
+ 'rArr' => '⇒',
+ 'rarr' => '→',
+ 'rarrap' => '⥵',
+ 'rarrb' => '⇥',
+ 'rarrbfs' => '⤠',
+ 'rarrc' => '⤳',
+ 'rarrfs' => '⤞',
+ 'rarrhk' => '↪',
+ 'rarrlp' => '↬',
+ 'rarrpl' => '⥅',
+ 'rarrsim' => '⥴',
+ 'Rarrtl' => '⤖',
+ 'rarrtl' => '↣',
+ 'rarrw' => '↝',
+ 'rAtail' => '⤜',
+ 'ratail' => '⤚',
+ 'ratio' => '∶',
+ 'rationals' => 'ℚ',
+ 'RBarr' => '⤐',
+ 'rBarr' => '⤏',
+ 'rbarr' => '⤍',
+ 'rbbrk' => '❳',
+ 'rbrace' => '}',
+ 'rbrack' => ']',
+ 'rbrke' => '⦌',
+ 'rbrksld' => '⦎',
+ 'rbrkslu' => '⦐',
+ 'Rcaron' => 'Ř',
+ 'rcaron' => 'ř',
+ 'Rcedil' => 'Ŗ',
+ 'rcedil' => 'ŗ',
+ 'rceil' => '⌉',
+ 'rcub' => '}',
+ 'Rcy' => 'Р',
+ 'rcy' => 'р',
+ 'rdca' => '⤷',
+ 'rdldhar' => '⥩',
+ 'rdquo' => '”',
+ 'rdquor' => '”',
+ 'rdsh' => '↳',
+ 'Re' => 'ℜ',
+ 'real' => 'ℜ',
+ 'realine' => 'ℛ',
+ 'realpart' => 'ℜ',
+ 'reals' => 'ℝ',
+ 'rect' => '▭',
+ 'REG' => '®',
+ 'RE' => '®',
+ 'reg' => '®',
+ 're' => '®',
+ 'ReverseElement' => '∋',
+ 'ReverseEquilibrium' => '⇋',
+ 'ReverseUpEquilibrium' => '⥯',
+ 'rfisht' => '⥽',
+ 'rfloor' => '⌋',
+ 'Rfr' => 'ℜ',
+ 'rfr' => '𝔯',
+ 'rHar' => '⥤',
+ 'rhard' => '⇁',
+ 'rharu' => '⇀',
+ 'rharul' => '⥬',
+ 'Rho' => 'Ρ',
+ 'rho' => 'ρ',
+ 'rhov' => 'ϱ',
+ 'RightAngleBracket' => '⟩',
+ 'RightArrow' => '→',
+ 'Rightarrow' => '⇒',
+ 'rightarrow' => '→',
+ 'RightArrowBar' => '⇥',
+ 'RightArrowLeftArrow' => '⇄',
+ 'rightarrowtail' => '↣',
+ 'RightCeiling' => '⌉',
+ 'RightDoubleBracket' => '⟧',
+ 'RightDownTeeVector' => '⥝',
+ 'RightDownVector' => '⇂',
+ 'RightDownVectorBar' => '⥕',
+ 'RightFloor' => '⌋',
+ 'rightharpoondown' => '⇁',
+ 'rightharpoonup' => '⇀',
+ 'rightleftarrows' => '⇄',
+ 'rightleftharpoons' => '⇌',
+ 'rightrightarrows' => '⇉',
+ 'rightsquigarrow' => '↝',
+ 'RightTee' => '⊢',
+ 'RightTeeArrow' => '↦',
+ 'RightTeeVector' => '⥛',
+ 'rightthreetimes' => '⋌',
+ 'RightTriangle' => '⊳',
+ 'RightTriangleBar' => '⧐',
+ 'RightTriangleEqual' => '⊵',
+ 'RightUpDownVector' => '⥏',
+ 'RightUpTeeVector' => '⥜',
+ 'RightUpVector' => '↾',
+ 'RightUpVectorBar' => '⥔',
+ 'RightVector' => '⇀',
+ 'RightVectorBar' => '⥓',
+ 'ring' => '˚',
+ 'risingdotseq' => '≓',
+ 'rlarr' => '⇄',
+ 'rlhar' => '⇌',
+ 'rlm' => '',
+ 'rmoust' => '⎱',
+ 'rmoustache' => '⎱',
+ 'rnmid' => '⫮',
+ 'roang' => '⟭',
+ 'roarr' => '⇾',
+ 'robrk' => '⟧',
+ 'ropar' => '⦆',
+ 'Ropf' => 'ℝ',
+ 'ropf' => '𝕣',
+ 'roplus' => '⨮',
+ 'rotimes' => '⨵',
+ 'RoundImplies' => '⥰',
+ 'rpar' => ')',
+ 'rpargt' => '⦔',
+ 'rppolint' => '⨒',
+ 'rrarr' => '⇉',
+ 'Rrightarrow' => '⇛',
+ 'rsaquo' => '›',
+ 'Rscr' => 'ℛ',
+ 'rscr' => '𝓇',
+ 'Rsh' => '↱',
+ 'rsh' => '↱',
+ 'rsqb' => ']',
+ 'rsquo' => '’',
+ 'rsquor' => '’',
+ 'rthree' => '⋌',
+ 'rtimes' => '⋊',
+ 'rtri' => '▹',
+ 'rtrie' => '⊵',
+ 'rtrif' => '▸',
+ 'rtriltri' => '⧎',
+ 'RuleDelayed' => '⧴',
+ 'ruluhar' => '⥨',
+ 'rx' => '℞',
+ 'Sacute' => 'Ś',
+ 'sacute' => 'ś',
+ 'sbquo' => '‚',
+ 'Sc' => '⪼',
+ 'sc' => '≻',
+ 'scap' => '⪸',
+ 'Scaron' => 'Š',
+ 'scaron' => 'š',
+ 'sccue' => '≽',
+ 'scE' => '⪴',
+ 'sce' => '⪰',
+ 'Scedil' => 'Ş',
+ 'scedil' => 'ş',
+ 'Scirc' => 'Ŝ',
+ 'scirc' => 'ŝ',
+ 'scnap' => '⪺',
+ 'scnE' => '⪶',
+ 'scnsim' => '⋩',
+ 'scpolint' => '⨓',
+ 'scsim' => '≿',
+ 'Scy' => 'С',
+ 'scy' => 'с',
+ 'sdot' => '⋅',
+ 'sdotb' => '⊡',
+ 'sdote' => '⩦',
+ 'searhk' => '⤥',
+ 'seArr' => '⇘',
+ 'searr' => '↘',
+ 'searrow' => '↘',
+ 'sect' => '§',
+ 'sec' => '§',
+ 'semi' => ';',
+ 'seswar' => '⤩',
+ 'setminus' => '∖',
+ 'setmn' => '∖',
+ 'sext' => '✶',
+ 'Sfr' => '𝔖',
+ 'sfr' => '𝔰',
+ 'sfrown' => '⌢',
+ 'sharp' => '♯',
+ 'SHCHcy' => 'Щ',
+ 'shchcy' => 'щ',
+ 'SHcy' => 'Ш',
+ 'shcy' => 'ш',
+ 'ShortDownArrow' => '↓',
+ 'ShortLeftArrow' => '←',
+ 'shortmid' => '∣',
+ 'shortparallel' => '∥',
+ 'ShortRightArrow' => '→',
+ 'ShortUpArrow' => '↑',
+ 'shy' => '',
+ 'sh' => '',
+ 'Sigma' => 'Σ',
+ 'sigma' => 'σ',
+ 'sigmaf' => 'ς',
+ 'sigmav' => 'ς',
+ 'sim' => '∼',
+ 'simdot' => '⩪',
+ 'sime' => '≃',
+ 'simeq' => '≃',
+ 'simg' => '⪞',
+ 'simgE' => '⪠',
+ 'siml' => '⪝',
+ 'simlE' => '⪟',
+ 'simne' => '≆',
+ 'simplus' => '⨤',
+ 'simrarr' => '⥲',
+ 'slarr' => '←',
+ 'SmallCircle' => '∘',
+ 'smallsetminus' => '∖',
+ 'smashp' => '⨳',
+ 'smeparsl' => '⧤',
+ 'smid' => '∣',
+ 'smile' => '⌣',
+ 'smt' => '⪪',
+ 'smte' => '⪬',
+ 'smtes' => '⪬︀',
+ 'SOFTcy' => 'Ь',
+ 'softcy' => 'ь',
+ 'sol' => '/',
+ 'solb' => '⧄',
+ 'solbar' => '⌿',
+ 'Sopf' => '𝕊',
+ 'sopf' => '𝕤',
+ 'spades' => '♠',
+ 'spadesuit' => '♠',
+ 'spar' => '∥',
+ 'sqcap' => '⊓',
+ 'sqcaps' => '⊓︀',
+ 'sqcup' => '⊔',
+ 'sqcups' => '⊔︀',
+ 'Sqrt' => '√',
+ 'sqsub' => '⊏',
+ 'sqsube' => '⊑',
+ 'sqsubset' => '⊏',
+ 'sqsubseteq' => '⊑',
+ 'sqsup' => '⊐',
+ 'sqsupe' => '⊒',
+ 'sqsupset' => '⊐',
+ 'sqsupseteq' => '⊒',
+ 'squ' => '□',
+ 'Square' => '□',
+ 'square' => '□',
+ 'SquareIntersection' => '⊓',
+ 'SquareSubset' => '⊏',
+ 'SquareSubsetEqual' => '⊑',
+ 'SquareSuperset' => '⊐',
+ 'SquareSupersetEqual' => '⊒',
+ 'SquareUnion' => '⊔',
+ 'squarf' => '▪',
+ 'squf' => '▪',
+ 'srarr' => '→',
+ 'Sscr' => '𝒮',
+ 'sscr' => '𝓈',
+ 'ssetmn' => '∖',
+ 'ssmile' => '⌣',
+ 'sstarf' => '⋆',
+ 'Star' => '⋆',
+ 'star' => '☆',
+ 'starf' => '★',
+ 'straightepsilon' => 'ϵ',
+ 'straightphi' => 'ϕ',
+ 'strns' => '¯',
+ 'Sub' => '⋐',
+ 'sub' => '⊂',
+ 'subdot' => '⪽',
+ 'subE' => '⫅',
+ 'sube' => '⊆',
+ 'subedot' => '⫃',
+ 'submult' => '⫁',
+ 'subnE' => '⫋',
+ 'subne' => '⊊',
+ 'subplus' => '⪿',
+ 'subrarr' => '⥹',
+ 'Subset' => '⋐',
+ 'subset' => '⊂',
+ 'subseteq' => '⊆',
+ 'subseteqq' => '⫅',
+ 'SubsetEqual' => '⊆',
+ 'subsetneq' => '⊊',
+ 'subsetneqq' => '⫋',
+ 'subsim' => '⫇',
+ 'subsub' => '⫕',
+ 'subsup' => '⫓',
+ 'succ' => '≻',
+ 'succapprox' => '⪸',
+ 'succcurlyeq' => '≽',
+ 'Succeeds' => '≻',
+ 'SucceedsEqual' => '⪰',
+ 'SucceedsSlantEqual' => '≽',
+ 'SucceedsTilde' => '≿',
+ 'succeq' => '⪰',
+ 'succnapprox' => '⪺',
+ 'succneqq' => '⪶',
+ 'succnsim' => '⋩',
+ 'succsim' => '≿',
+ 'SuchThat' => '∋',
+ 'Sum' => '∑',
+ 'sum' => '∑',
+ 'sung' => '♪',
+ 'Sup' => '⋑',
+ 'sup' => '³',
+ 'sup1' => '¹',
+ 'sup2' => '²',
+ 'sup3' => '³',
+ 'supdot' => '⪾',
+ 'supdsub' => '⫘',
+ 'supE' => '⫆',
+ 'supe' => '⊇',
+ 'supedot' => '⫄',
+ 'Superset' => '⊃',
+ 'SupersetEqual' => '⊇',
+ 'suphsol' => '⟉',
+ 'suphsub' => '⫗',
+ 'suplarr' => '⥻',
+ 'supmult' => '⫂',
+ 'supnE' => '⫌',
+ 'supne' => '⊋',
+ 'supplus' => '⫀',
+ 'Supset' => '⋑',
+ 'supset' => '⊃',
+ 'supseteq' => '⊇',
+ 'supseteqq' => '⫆',
+ 'supsetneq' => '⊋',
+ 'supsetneqq' => '⫌',
+ 'supsim' => '⫈',
+ 'supsub' => '⫔',
+ 'supsup' => '⫖',
+ 'swarhk' => '⤦',
+ 'swArr' => '⇙',
+ 'swarr' => '↙',
+ 'swarrow' => '↙',
+ 'swnwar' => '⤪',
+ 'szlig' => 'ß',
+ 'szli' => 'ß',
+ 'Tab' => ' ',
+ 'target' => '⌖',
+ 'Tau' => 'Τ',
+ 'tau' => 'τ',
+ 'tbrk' => '⎴',
+ 'Tcaron' => 'Ť',
+ 'tcaron' => 'ť',
+ 'Tcedil' => 'Ţ',
+ 'tcedil' => 'ţ',
+ 'Tcy' => 'Т',
+ 'tcy' => 'т',
+ 'tdot' => '⃛',
+ 'telrec' => '⌕',
+ 'Tfr' => '𝔗',
+ 'tfr' => '𝔱',
+ 'there4' => '∴',
+ 'Therefore' => '∴',
+ 'therefore' => '∴',
+ 'Theta' => 'Θ',
+ 'theta' => 'θ',
+ 'thetasym' => 'ϑ',
+ 'thetav' => 'ϑ',
+ 'thickapprox' => '≈',
+ 'thicksim' => '∼',
+ 'ThickSpace' => ' ',
+ 'thinsp' => ' ',
+ 'ThinSpace' => ' ',
+ 'thkap' => '≈',
+ 'thksim' => '∼',
+ 'THORN' => 'Þ',
+ 'THOR' => 'Þ',
+ 'thorn' => 'þ',
+ 'thor' => 'þ',
+ 'Tilde' => '∼',
+ 'tilde' => '˜',
+ 'TildeEqual' => '≃',
+ 'TildeFullEqual' => '≅',
+ 'TildeTilde' => '≈',
+ 'times' => '×',
+ 'time' => '×',
+ 'timesb' => '⊠',
+ 'timesbar' => '⨱',
+ 'timesd' => '⨰',
+ 'tint' => '∭',
+ 'toea' => '⤨',
+ 'top' => '⊤',
+ 'topbot' => '⌶',
+ 'topcir' => '⫱',
+ 'Topf' => '𝕋',
+ 'topf' => '𝕥',
+ 'topfork' => '⫚',
+ 'tosa' => '⤩',
+ 'tprime' => '‴',
+ 'TRADE' => '™',
+ 'trade' => '™',
+ 'triangle' => '▵',
+ 'triangledown' => '▿',
+ 'triangleleft' => '◃',
+ 'trianglelefteq' => '⊴',
+ 'triangleq' => '≜',
+ 'triangleright' => '▹',
+ 'trianglerighteq' => '⊵',
+ 'tridot' => '◬',
+ 'trie' => '≜',
+ 'triminus' => '⨺',
+ 'TripleDot' => '⃛',
+ 'triplus' => '⨹',
+ 'trisb' => '⧍',
+ 'tritime' => '⨻',
+ 'trpezium' => '⏢',
+ 'Tscr' => '𝒯',
+ 'tscr' => '𝓉',
+ 'TScy' => 'Ц',
+ 'tscy' => 'ц',
+ 'TSHcy' => 'Ћ',
+ 'tshcy' => 'ћ',
+ 'Tstrok' => 'Ŧ',
+ 'tstrok' => 'ŧ',
+ 'twixt' => '≬',
+ 'twoheadleftarrow' => '↞',
+ 'twoheadrightarrow' => '↠',
+ 'Uacute' => 'Ú',
+ 'Uacut' => 'Ú',
+ 'uacute' => 'ú',
+ 'uacut' => 'ú',
+ 'Uarr' => '↟',
+ 'uArr' => '⇑',
+ 'uarr' => '↑',
+ 'Uarrocir' => '⥉',
+ 'Ubrcy' => 'Ў',
+ 'ubrcy' => 'ў',
+ 'Ubreve' => 'Ŭ',
+ 'ubreve' => 'ŭ',
+ 'Ucirc' => 'Û',
+ 'Ucir' => 'Û',
+ 'ucirc' => 'û',
+ 'ucir' => 'û',
+ 'Ucy' => 'У',
+ 'ucy' => 'у',
+ 'udarr' => '⇅',
+ 'Udblac' => 'Ű',
+ 'udblac' => 'ű',
+ 'udhar' => '⥮',
+ 'ufisht' => '⥾',
+ 'Ufr' => '𝔘',
+ 'ufr' => '𝔲',
+ 'Ugrave' => 'Ù',
+ 'Ugrav' => 'Ù',
+ 'ugrave' => 'ù',
+ 'ugrav' => 'ù',
+ 'uHar' => '⥣',
+ 'uharl' => '↿',
+ 'uharr' => '↾',
+ 'uhblk' => '▀',
+ 'ulcorn' => '⌜',
+ 'ulcorner' => '⌜',
+ 'ulcrop' => '⌏',
+ 'ultri' => '◸',
+ 'Umacr' => 'Ū',
+ 'umacr' => 'ū',
+ 'uml' => '¨',
+ 'um' => '¨',
+ 'UnderBar' => '_',
+ 'UnderBrace' => '⏟',
+ 'UnderBracket' => '⎵',
+ 'UnderParenthesis' => '⏝',
+ 'Union' => '⋃',
+ 'UnionPlus' => '⊎',
+ 'Uogon' => 'Ų',
+ 'uogon' => 'ų',
+ 'Uopf' => '𝕌',
+ 'uopf' => '𝕦',
+ 'UpArrow' => '↑',
+ 'Uparrow' => '⇑',
+ 'uparrow' => '↑',
+ 'UpArrowBar' => '⤒',
+ 'UpArrowDownArrow' => '⇅',
+ 'UpDownArrow' => '↕',
+ 'Updownarrow' => '⇕',
+ 'updownarrow' => '↕',
+ 'UpEquilibrium' => '⥮',
+ 'upharpoonleft' => '↿',
+ 'upharpoonright' => '↾',
+ 'uplus' => '⊎',
+ 'UpperLeftArrow' => '↖',
+ 'UpperRightArrow' => '↗',
+ 'Upsi' => 'ϒ',
+ 'upsi' => 'υ',
+ 'upsih' => 'ϒ',
+ 'Upsilon' => 'Υ',
+ 'upsilon' => 'υ',
+ 'UpTee' => '⊥',
+ 'UpTeeArrow' => '↥',
+ 'upuparrows' => '⇈',
+ 'urcorn' => '⌝',
+ 'urcorner' => '⌝',
+ 'urcrop' => '⌎',
+ 'Uring' => 'Ů',
+ 'uring' => 'ů',
+ 'urtri' => '◹',
+ 'Uscr' => '𝒰',
+ 'uscr' => '𝓊',
+ 'utdot' => '⋰',
+ 'Utilde' => 'Ũ',
+ 'utilde' => 'ũ',
+ 'utri' => '▵',
+ 'utrif' => '▴',
+ 'uuarr' => '⇈',
+ 'Uuml' => 'Ü',
+ 'Uum' => 'Ü',
+ 'uuml' => 'ü',
+ 'uum' => 'ü',
+ 'uwangle' => '⦧',
+ 'vangrt' => '⦜',
+ 'varepsilon' => 'ϵ',
+ 'varkappa' => 'ϰ',
+ 'varnothing' => '∅',
+ 'varphi' => 'ϕ',
+ 'varpi' => 'ϖ',
+ 'varpropto' => '∝',
+ 'vArr' => '⇕',
+ 'varr' => '↕',
+ 'varrho' => 'ϱ',
+ 'varsigma' => 'ς',
+ 'varsubsetneq' => '⊊︀',
+ 'varsubsetneqq' => '⫋︀',
+ 'varsupsetneq' => '⊋︀',
+ 'varsupsetneqq' => '⫌︀',
+ 'vartheta' => 'ϑ',
+ 'vartriangleleft' => '⊲',
+ 'vartriangleright' => '⊳',
+ 'Vbar' => '⫫',
+ 'vBar' => '⫨',
+ 'vBarv' => '⫩',
+ 'Vcy' => 'В',
+ 'vcy' => 'в',
+ 'VDash' => '⊫',
+ 'Vdash' => '⊩',
+ 'vDash' => '⊨',
+ 'vdash' => '⊢',
+ 'Vdashl' => '⫦',
+ 'Vee' => '⋁',
+ 'vee' => '∨',
+ 'veebar' => '⊻',
+ 'veeeq' => '≚',
+ 'vellip' => '⋮',
+ 'Verbar' => '‖',
+ 'verbar' => '|',
+ 'Vert' => '‖',
+ 'vert' => '|',
+ 'VerticalBar' => '∣',
+ 'VerticalLine' => '|',
+ 'VerticalSeparator' => '❘',
+ 'VerticalTilde' => '≀',
+ 'VeryThinSpace' => ' ',
+ 'Vfr' => '𝔙',
+ 'vfr' => '𝔳',
+ 'vltri' => '⊲',
+ 'vnsub' => '⊂⃒',
+ 'vnsup' => '⊃⃒',
+ 'Vopf' => '𝕍',
+ 'vopf' => '𝕧',
+ 'vprop' => '∝',
+ 'vrtri' => '⊳',
+ 'Vscr' => '𝒱',
+ 'vscr' => '𝓋',
+ 'vsubnE' => '⫋︀',
+ 'vsubne' => '⊊︀',
+ 'vsupnE' => '⫌︀',
+ 'vsupne' => '⊋︀',
+ 'Vvdash' => '⊪',
+ 'vzigzag' => '⦚',
+ 'Wcirc' => 'Ŵ',
+ 'wcirc' => 'ŵ',
+ 'wedbar' => '⩟',
+ 'Wedge' => '⋀',
+ 'wedge' => '∧',
+ 'wedgeq' => '≙',
+ 'weierp' => '℘',
+ 'Wfr' => '𝔚',
+ 'wfr' => '𝔴',
+ 'Wopf' => '𝕎',
+ 'wopf' => '𝕨',
+ 'wp' => '℘',
+ 'wr' => '≀',
+ 'wreath' => '≀',
+ 'Wscr' => '𝒲',
+ 'wscr' => '𝓌',
+ 'xcap' => '⋂',
+ 'xcirc' => '◯',
+ 'xcup' => '⋃',
+ 'xdtri' => '▽',
+ 'Xfr' => '𝔛',
+ 'xfr' => '𝔵',
+ 'xhArr' => '⟺',
+ 'xharr' => '⟷',
+ 'Xi' => 'Ξ',
+ 'xi' => 'ξ',
+ 'xlArr' => '⟸',
+ 'xlarr' => '⟵',
+ 'xmap' => '⟼',
+ 'xnis' => '⋻',
+ 'xodot' => '⨀',
+ 'Xopf' => '𝕏',
+ 'xopf' => '𝕩',
+ 'xoplus' => '⨁',
+ 'xotime' => '⨂',
+ 'xrArr' => '⟹',
+ 'xrarr' => '⟶',
+ 'Xscr' => '𝒳',
+ 'xscr' => '𝓍',
+ 'xsqcup' => '⨆',
+ 'xuplus' => '⨄',
+ 'xutri' => '△',
+ 'xvee' => '⋁',
+ 'xwedge' => '⋀',
+ 'Yacute' => 'Ý',
+ 'Yacut' => 'Ý',
+ 'yacute' => 'ý',
+ 'yacut' => 'ý',
+ 'YAcy' => 'Я',
+ 'yacy' => 'я',
+ 'Ycirc' => 'Ŷ',
+ 'ycirc' => 'ŷ',
+ 'Ycy' => 'Ы',
+ 'ycy' => 'ы',
+ 'yen' => '¥',
+ 'ye' => '¥',
+ 'Yfr' => '𝔜',
+ 'yfr' => '𝔶',
+ 'YIcy' => 'Ї',
+ 'yicy' => 'ї',
+ 'Yopf' => '𝕐',
+ 'yopf' => '𝕪',
+ 'Yscr' => '𝒴',
+ 'yscr' => '𝓎',
+ 'YUcy' => 'Ю',
+ 'yucy' => 'ю',
+ 'Yuml' => 'Ÿ',
+ 'yuml' => 'ÿ',
+ 'yum' => 'ÿ',
+ 'Zacute' => 'Ź',
+ 'zacute' => 'ź',
+ 'Zcaron' => 'Ž',
+ 'zcaron' => 'ž',
+ 'Zcy' => 'З',
+ 'zcy' => 'з',
+ 'Zdot' => 'Ż',
+ 'zdot' => 'ż',
+ 'zeetrf' => 'ℨ',
+ 'ZeroWidthSpace' => '',
+ 'Zeta' => 'Ζ',
+ 'zeta' => 'ζ',
+ 'Zfr' => 'ℨ',
+ 'zfr' => '𝔷',
+ 'ZHcy' => 'Ж',
+ 'zhcy' => 'ж',
+ 'zigrarr' => '⇝',
+ 'Zopf' => 'ℤ',
+ 'zopf' => '𝕫',
+ 'Zscr' => '𝒵',
+ 'zscr' => '𝓏',
+ 'zwj' => '',
+ 'zwnj' => '',
+ );
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Exception.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Exception.php
new file mode 100644
index 000000000..64e97e6ff
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Exception.php
@@ -0,0 +1,10 @@
+ self::NAMESPACE_HTML,
+ 'svg' => self::NAMESPACE_SVG,
+ 'math' => self::NAMESPACE_MATHML,
+ );
+
+ /**
+ * Holds the always available namespaces (which does not require the XMLNS declaration).
+ *
+ * @var array
+ */
+ protected $implicitNamespaces = array(
+ 'xml' => self::NAMESPACE_XML,
+ 'xmlns' => self::NAMESPACE_XMLNS,
+ 'xlink' => self::NAMESPACE_XLINK,
+ );
+
+ /**
+ * Holds a stack of currently active namespaces.
+ *
+ * @var array
+ */
+ protected $nsStack = array();
+
+ /**
+ * Holds the number of namespaces declared by a node.
+ *
+ * @var array
+ */
+ protected $pushes = array();
+
+ /**
+ * Defined in 8.2.5.
+ */
+ const IM_INITIAL = 0;
+
+ const IM_BEFORE_HTML = 1;
+
+ const IM_BEFORE_HEAD = 2;
+
+ const IM_IN_HEAD = 3;
+
+ const IM_IN_HEAD_NOSCRIPT = 4;
+
+ const IM_AFTER_HEAD = 5;
+
+ const IM_IN_BODY = 6;
+
+ const IM_TEXT = 7;
+
+ const IM_IN_TABLE = 8;
+
+ const IM_IN_TABLE_TEXT = 9;
+
+ const IM_IN_CAPTION = 10;
+
+ const IM_IN_COLUMN_GROUP = 11;
+
+ const IM_IN_TABLE_BODY = 12;
+
+ const IM_IN_ROW = 13;
+
+ const IM_IN_CELL = 14;
+
+ const IM_IN_SELECT = 15;
+
+ const IM_IN_SELECT_IN_TABLE = 16;
+
+ const IM_AFTER_BODY = 17;
+
+ const IM_IN_FRAMESET = 18;
+
+ const IM_AFTER_FRAMESET = 19;
+
+ const IM_AFTER_AFTER_BODY = 20;
+
+ const IM_AFTER_AFTER_FRAMESET = 21;
+
+ const IM_IN_SVG = 22;
+
+ const IM_IN_MATHML = 23;
+
+ protected $options = array();
+
+ protected $stack = array();
+
+ protected $current; // Pointer in the tag hierarchy.
+ protected $rules;
+ protected $doc;
+
+ protected $frag;
+
+ protected $processor;
+
+ protected $insertMode = 0;
+
+ /**
+ * Track if we are in an element that allows only inline child nodes.
+ *
+ * @var string|null
+ */
+ protected $onlyInline;
+
+ /**
+ * Quirks mode is enabled by default.
+ * Any document that is missing the DT will be considered to be in quirks mode.
+ */
+ protected $quirks = true;
+
+ protected $errors = array();
+
+ public function __construct($isFragment = false, array $options = array())
+ {
+ $this->options = $options;
+
+ if (isset($options[self::OPT_TARGET_DOC])) {
+ $this->doc = $options[self::OPT_TARGET_DOC];
+ } else {
+ $impl = new \DOMImplementation();
+ // XXX:
+ // Create the doctype. For now, we are always creating HTML5
+ // documents, and attempting to up-convert any older DTDs to HTML5.
+ $dt = $impl->createDocumentType('html');
+ // $this->doc = \DOMImplementation::createDocument(NULL, 'html', $dt);
+ $this->doc = $impl->createDocument(null, '', $dt);
+ $this->doc->encoding = !empty($options['encoding']) ? $options['encoding'] : 'UTF-8';
+ }
+
+ $this->errors = array();
+
+ $this->current = $this->doc; // ->documentElement;
+
+ // Create a rules engine for tags.
+ $this->rules = new TreeBuildingRules();
+
+ $implicitNS = array();
+ if (isset($this->options[self::OPT_IMPLICIT_NS])) {
+ $implicitNS = $this->options[self::OPT_IMPLICIT_NS];
+ } elseif (isset($this->options['implicitNamespaces'])) {
+ $implicitNS = $this->options['implicitNamespaces'];
+ }
+
+ // Fill $nsStack with the defalut HTML5 namespaces, plus the "implicitNamespaces" array taken form $options
+ array_unshift($this->nsStack, $implicitNS + array('' => self::NAMESPACE_HTML) + $this->implicitNamespaces);
+
+ if ($isFragment) {
+ $this->insertMode = static::IM_IN_BODY;
+ $this->frag = $this->doc->createDocumentFragment();
+ $this->current = $this->frag;
+ }
+ }
+
+ /**
+ * Get the document.
+ */
+ public function document()
+ {
+ return $this->doc;
+ }
+
+ /**
+ * Get the DOM fragment for the body.
+ *
+ * This returns a DOMNodeList because a fragment may have zero or more
+ * DOMNodes at its root.
+ *
+ * @see http://www.w3.org/TR/2012/CR-html5-20121217/syntax.html#concept-frag-parse-context
+ *
+ * @return \DOMDocumentFragment
+ */
+ public function fragment()
+ {
+ return $this->frag;
+ }
+
+ /**
+ * Provide an instruction processor.
+ *
+ * This is used for handling Processor Instructions as they are
+ * inserted. If omitted, PI's are inserted directly into the DOM tree.
+ *
+ * @param InstructionProcessor $proc
+ */
+ public function setInstructionProcessor(InstructionProcessor $proc)
+ {
+ $this->processor = $proc;
+ }
+
+ public function doctype($name, $idType = 0, $id = null, $quirks = false)
+ {
+ // This is used solely for setting quirks mode. Currently we don't
+ // try to preserve the inbound DT. We convert it to HTML5.
+ $this->quirks = $quirks;
+
+ if ($this->insertMode > static::IM_INITIAL) {
+ $this->parseError('Illegal placement of DOCTYPE tag. Ignoring: ' . $name);
+
+ return;
+ }
+
+ $this->insertMode = static::IM_BEFORE_HTML;
+ }
+
+ /**
+ * Process the start tag.
+ *
+ * @todo - XMLNS namespace handling (we need to parse, even if it's not valid)
+ * - XLink, MathML and SVG namespace handling
+ * - Omission rules: 8.1.2.4 Optional tags
+ *
+ * @param string $name
+ * @param array $attributes
+ * @param bool $selfClosing
+ *
+ * @return int
+ */
+ public function startTag($name, $attributes = array(), $selfClosing = false)
+ {
+ $lname = $this->normalizeTagName($name);
+
+ // Make sure we have an html element.
+ if (!$this->doc->documentElement && 'html' !== $name && !$this->frag) {
+ $this->startTag('html');
+ }
+
+ // Set quirks mode if we're at IM_INITIAL with no doctype.
+ if ($this->insertMode === static::IM_INITIAL) {
+ $this->quirks = true;
+ $this->parseError('No DOCTYPE specified.');
+ }
+
+ // SPECIAL TAG HANDLING:
+ // Spec says do this, and "don't ask."
+ // find the spec where this is defined... looks problematic
+ if ('image' === $name && !($this->insertMode === static::IM_IN_SVG || $this->insertMode === static::IM_IN_MATHML)) {
+ $name = 'img';
+ }
+
+ // Autoclose p tags where appropriate.
+ if ($this->insertMode >= static::IM_IN_BODY && Elements::isA($name, Elements::AUTOCLOSE_P)) {
+ $this->autoclose('p');
+ }
+
+ // Set insert mode:
+ switch ($name) {
+ case 'html':
+ $this->insertMode = static::IM_BEFORE_HEAD;
+ break;
+ case 'head':
+ if ($this->insertMode > static::IM_BEFORE_HEAD) {
+ $this->parseError('Unexpected head tag outside of head context.');
+ } else {
+ $this->insertMode = static::IM_IN_HEAD;
+ }
+ break;
+ case 'body':
+ $this->insertMode = static::IM_IN_BODY;
+ break;
+ case 'svg':
+ $this->insertMode = static::IM_IN_SVG;
+ break;
+ case 'math':
+ $this->insertMode = static::IM_IN_MATHML;
+ break;
+ case 'noscript':
+ if ($this->insertMode === static::IM_IN_HEAD) {
+ $this->insertMode = static::IM_IN_HEAD_NOSCRIPT;
+ }
+ break;
+ }
+
+ // Special case handling for SVG.
+ if ($this->insertMode === static::IM_IN_SVG) {
+ $lname = Elements::normalizeSvgElement($lname);
+ }
+
+ $pushes = 0;
+ // when we found a tag thats appears inside $nsRoots, we have to switch the defalut namespace
+ if (isset($this->nsRoots[$lname]) && $this->nsStack[0][''] !== $this->nsRoots[$lname]) {
+ array_unshift($this->nsStack, array(
+ '' => $this->nsRoots[$lname],
+ ) + $this->nsStack[0]);
+ ++$pushes;
+ }
+ $needsWorkaround = false;
+ if (isset($this->options['xmlNamespaces']) && $this->options['xmlNamespaces']) {
+ // when xmlNamespaces is true a and we found a 'xmlns' or 'xmlns:*' attribute, we should add a new item to the $nsStack
+ foreach ($attributes as $aName => $aVal) {
+ if ('xmlns' === $aName) {
+ $needsWorkaround = $aVal;
+ array_unshift($this->nsStack, array(
+ '' => $aVal,
+ ) + $this->nsStack[0]);
+ ++$pushes;
+ } elseif ('xmlns' === (($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : '')) {
+ array_unshift($this->nsStack, array(
+ substr($aName, $pos + 1) => $aVal,
+ ) + $this->nsStack[0]);
+ ++$pushes;
+ }
+ }
+ }
+
+ if ($this->onlyInline && Elements::isA($lname, Elements::BLOCK_TAG)) {
+ $this->autoclose($this->onlyInline);
+ $this->onlyInline = null;
+ }
+
+ try {
+ $prefix = ($pos = strpos($lname, ':')) ? substr($lname, 0, $pos) : '';
+
+ if (false !== $needsWorkaround) {
+ $xml = "<$lname xmlns=\"$needsWorkaround\" " . (strlen($prefix) && isset($this->nsStack[0][$prefix]) ? ("xmlns:$prefix=\"" . $this->nsStack[0][$prefix] . '"') : '') . '/>';
+
+ $frag = new \DOMDocument('1.0', 'UTF-8');
+ $frag->loadXML($xml);
+
+ $ele = $this->doc->importNode($frag->documentElement, true);
+ } else {
+ if (!isset($this->nsStack[0][$prefix]) || ('' === $prefix && isset($this->options[self::OPT_DISABLE_HTML_NS]) && $this->options[self::OPT_DISABLE_HTML_NS])) {
+ $ele = $this->doc->createElement($lname);
+ } else {
+ $ele = $this->doc->createElementNS($this->nsStack[0][$prefix], $lname);
+ }
+ }
+ } catch (\DOMException $e) {
+ $this->parseError("Illegal tag name: <$lname>. Replaced with .");
+ $ele = $this->doc->createElement('invalid');
+ }
+
+ if (Elements::isA($lname, Elements::BLOCK_ONLY_INLINE)) {
+ $this->onlyInline = $lname;
+ }
+
+ // When we add some namespacess, we have to track them. Later, when "endElement" is invoked, we have to remove them.
+ // When we are on a void tag, we do not need to care about namesapce nesting.
+ if ($pushes > 0 && !Elements::isA($name, Elements::VOID_TAG)) {
+ // PHP tends to free the memory used by DOM,
+ // to avoid spl_object_hash collisions whe have to avoid garbage collection of $ele storing it into $pushes
+ // see https://bugs.php.net/bug.php?id=67459
+ $this->pushes[spl_object_hash($ele)] = array($pushes, $ele);
+ }
+
+ foreach ($attributes as $aName => $aVal) {
+ // xmlns attributes can't be set
+ if ('xmlns' === $aName) {
+ continue;
+ }
+
+ if ($this->insertMode === static::IM_IN_SVG) {
+ $aName = Elements::normalizeSvgAttribute($aName);
+ } elseif ($this->insertMode === static::IM_IN_MATHML) {
+ $aName = Elements::normalizeMathMlAttribute($aName);
+ }
+
+ $aVal = (string) $aVal;
+
+ try {
+ $prefix = ($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : false;
+
+ if ('xmlns' === $prefix) {
+ $ele->setAttributeNS(self::NAMESPACE_XMLNS, $aName, $aVal);
+ } elseif (false !== $prefix && isset($this->nsStack[0][$prefix])) {
+ $ele->setAttributeNS($this->nsStack[0][$prefix], $aName, $aVal);
+ } else {
+ $ele->setAttribute($aName, $aVal);
+ }
+ } catch (\DOMException $e) {
+ $this->parseError("Illegal attribute name for tag $name. Ignoring: $aName");
+ continue;
+ }
+
+ // This is necessary on a non-DTD schema, like HTML5.
+ if ('id' === $aName) {
+ $ele->setIdAttribute('id', true);
+ }
+ }
+
+ if ($this->frag !== $this->current && $this->rules->hasRules($name)) {
+ // Some elements have special processing rules. Handle those separately.
+ $this->current = $this->rules->evaluate($ele, $this->current);
+ } else {
+ // Otherwise, it's a standard element.
+ $this->current->appendChild($ele);
+
+ if (!Elements::isA($name, Elements::VOID_TAG)) {
+ $this->current = $ele;
+ }
+
+ // Self-closing tags should only be respected on foreign elements
+ // (and are implied on void elements)
+ // See: https://www.w3.org/TR/html5/syntax.html#start-tags
+ if (Elements::isHtml5Element($name)) {
+ $selfClosing = false;
+ }
+ }
+
+ // This is sort of a last-ditch attempt to correct for cases where no head/body
+ // elements are provided.
+ if ($this->insertMode <= static::IM_BEFORE_HEAD && 'head' !== $name && 'html' !== $name) {
+ $this->insertMode = static::IM_IN_BODY;
+ }
+
+ // When we are on a void tag, we do not need to care about namesapce nesting,
+ // but we have to remove the namespaces pushed to $nsStack.
+ if ($pushes > 0 && Elements::isA($name, Elements::VOID_TAG)) {
+ // remove the namespaced definded by current node
+ for ($i = 0; $i < $pushes; ++$i) {
+ array_shift($this->nsStack);
+ }
+ }
+
+ if ($selfClosing) {
+ $this->endTag($name);
+ }
+
+ // Return the element mask, which the tokenizer can then use to set
+ // various processing rules.
+ return Elements::element($name);
+ }
+
+ public function endTag($name)
+ {
+ $lname = $this->normalizeTagName($name);
+
+ // Special case within 12.2.6.4.7: An end tag whose tag name is "br" should be treated as an opening tag
+ if ('br' === $name) {
+ $this->parseError('Closing tag encountered for void element br.');
+
+ $this->startTag('br');
+ }
+ // Ignore closing tags for other unary elements.
+ elseif (Elements::isA($name, Elements::VOID_TAG)) {
+ return;
+ }
+
+ if ($this->insertMode <= static::IM_BEFORE_HTML) {
+ // 8.2.5.4.2
+ if (in_array($name, array(
+ 'html',
+ 'br',
+ 'head',
+ 'title',
+ ))) {
+ $this->startTag('html');
+ $this->endTag($name);
+ $this->insertMode = static::IM_BEFORE_HEAD;
+
+ return;
+ }
+
+ // Ignore the tag.
+ $this->parseError('Illegal closing tag at global scope.');
+
+ return;
+ }
+
+ // Special case handling for SVG.
+ if ($this->insertMode === static::IM_IN_SVG) {
+ $lname = Elements::normalizeSvgElement($lname);
+ }
+
+ $cid = spl_object_hash($this->current);
+
+ // XXX: HTML has no parent. What do we do, though,
+ // if this element appears in the wrong place?
+ if ('html' === $lname) {
+ return;
+ }
+
+ // remove the namespaced definded by current node
+ if (isset($this->pushes[$cid])) {
+ for ($i = 0; $i < $this->pushes[$cid][0]; ++$i) {
+ array_shift($this->nsStack);
+ }
+ unset($this->pushes[$cid]);
+ }
+
+ if (!$this->autoclose($lname)) {
+ $this->parseError('Could not find closing tag for ' . $lname);
+ }
+
+ switch ($lname) {
+ case 'head':
+ $this->insertMode = static::IM_AFTER_HEAD;
+ break;
+ case 'body':
+ $this->insertMode = static::IM_AFTER_BODY;
+ break;
+ case 'svg':
+ case 'mathml':
+ $this->insertMode = static::IM_IN_BODY;
+ break;
+ }
+ }
+
+ public function comment($cdata)
+ {
+ // TODO: Need to handle case where comment appears outside of the HTML tag.
+ $node = $this->doc->createComment($cdata);
+ $this->current->appendChild($node);
+ }
+
+ public function text($data)
+ {
+ // XXX: Hmmm.... should we really be this strict?
+ if ($this->insertMode < static::IM_IN_HEAD) {
+ // Per '8.2.5.4.3 The "before head" insertion mode' the characters
+ // " \t\n\r\f" should be ignored but no mention of a parse error. This is
+ // practical as most documents contain these characters. Other text is not
+ // expected here so recording a parse error is necessary.
+ $dataTmp = trim($data, " \t\n\r\f");
+ if (!empty($dataTmp)) {
+ // fprintf(STDOUT, "Unexpected insert mode: %d", $this->insertMode);
+ $this->parseError('Unexpected text. Ignoring: ' . $dataTmp);
+ }
+
+ return;
+ }
+ // fprintf(STDOUT, "Appending text %s.", $data);
+ $node = $this->doc->createTextNode($data);
+ $this->current->appendChild($node);
+ }
+
+ public function eof()
+ {
+ // If the $current isn't the $root, do we need to do anything?
+ }
+
+ public function parseError($msg, $line = 0, $col = 0)
+ {
+ $this->errors[] = sprintf('Line %d, Col %d: %s', $line, $col, $msg);
+ }
+
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ public function cdata($data)
+ {
+ $node = $this->doc->createCDATASection($data);
+ $this->current->appendChild($node);
+ }
+
+ public function processingInstruction($name, $data = null)
+ {
+ // XXX: Ignore initial XML declaration, per the spec.
+ if ($this->insertMode === static::IM_INITIAL && 'xml' === strtolower($name)) {
+ return;
+ }
+
+ // Important: The processor may modify the current DOM tree however it sees fit.
+ if ($this->processor instanceof InstructionProcessor) {
+ $res = $this->processor->process($this->current, $name, $data);
+ if (!empty($res)) {
+ $this->current = $res;
+ }
+
+ return;
+ }
+
+ // Otherwise, this is just a dumb PI element.
+ $node = $this->doc->createProcessingInstruction($name, $data);
+
+ $this->current->appendChild($node);
+ }
+
+ // ==========================================================================
+ // UTILITIES
+ // ==========================================================================
+
+ /**
+ * Apply normalization rules to a tag name.
+ * See sections 2.9 and 8.1.2.
+ *
+ * @param string $tagName
+ *
+ * @return string The normalized tag name.
+ */
+ protected function normalizeTagName($tagName)
+ {
+ /*
+ * Section 2.9 suggests that we should not do this. if (strpos($name, ':') !== false) { // We know from the grammar that there must be at least one other // char besides :, since : is not a legal tag start. $parts = explode(':', $name); return array_pop($parts); }
+ */
+ return $tagName;
+ }
+
+ protected function quirksTreeResolver($name)
+ {
+ throw new \Exception('Not implemented.');
+ }
+
+ /**
+ * Automatically climb the tree and close the closest node with the matching $tag.
+ *
+ * @param string $tagName
+ *
+ * @return bool
+ */
+ protected function autoclose($tagName)
+ {
+ $working = $this->current;
+ do {
+ if (XML_ELEMENT_NODE !== $working->nodeType) {
+ return false;
+ }
+ if ($working->tagName === $tagName) {
+ $this->current = $working->parentNode;
+
+ return true;
+ }
+ } while ($working = $working->parentNode);
+
+ return false;
+ }
+
+ /**
+ * Checks if the given tagname is an ancestor of the present candidate.
+ *
+ * If $this->current or anything above $this->current matches the given tag
+ * name, this returns true.
+ *
+ * @param string $tagName
+ *
+ * @return bool
+ */
+ protected function isAncestor($tagName)
+ {
+ $candidate = $this->current;
+ while (XML_ELEMENT_NODE === $candidate->nodeType) {
+ if ($candidate->tagName === $tagName) {
+ return true;
+ }
+ $candidate = $candidate->parentNode;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the immediate parent element is of the given tagname.
+ *
+ * @param string $tagName
+ *
+ * @return bool
+ */
+ protected function isParent($tagName)
+ {
+ return $this->current->tagName === $tagName;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/EventHandler.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/EventHandler.php
new file mode 100644
index 000000000..9893a718b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/EventHandler.php
@@ -0,0 +1,114 @@
+).
+ *
+ * @return int one of the Tokenizer::TEXTMODE_* constants
+ */
+ public function startTag($name, $attributes = array(), $selfClosing = false);
+
+ /**
+ * An end-tag.
+ */
+ public function endTag($name);
+
+ /**
+ * A comment section (unparsed character data).
+ */
+ public function comment($cdata);
+
+ /**
+ * A unit of parsed character data.
+ *
+ * Entities in this text are *already decoded*.
+ */
+ public function text($cdata);
+
+ /**
+ * Indicates that the document has been entirely processed.
+ */
+ public function eof();
+
+ /**
+ * Emitted when the parser encounters an error condition.
+ */
+ public function parseError($msg, $line, $col);
+
+ /**
+ * A CDATA section.
+ *
+ * @param string $data
+ * The unparsed character data
+ */
+ public function cdata($data);
+
+ /**
+ * This is a holdover from the XML spec.
+ *
+ * While user agents don't get PIs, server-side does.
+ *
+ * @param string $name The name of the processor (e.g. 'php').
+ * @param string $data The unparsed data.
+ */
+ public function processingInstruction($name, $data = null);
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/FileInputStream.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/FileInputStream.php
new file mode 100644
index 000000000..b081ed96b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/FileInputStream.php
@@ -0,0 +1,33 @@
+errors = UTF8Utils::checkForIllegalCodepoints($data);
+
+ $data = $this->replaceLinefeeds($data);
+
+ $this->data = $data;
+ $this->char = 0;
+ $this->EOF = strlen($data);
+ }
+
+ /**
+ * Check if upcomming chars match the given sequence.
+ *
+ * This will read the stream for the $sequence. If it's
+ * found, this will return true. If not, return false.
+ * Since this unconsumes any chars it reads, the caller
+ * will still need to read the next sequence, even if
+ * this returns true.
+ *
+ * Example: $this->scanner->sequenceMatches('') will
+ * see if the input stream is at the start of a
+ * '' string.
+ *
+ * @param string $sequence
+ * @param bool $caseSensitive
+ *
+ * @return bool
+ */
+ public function sequenceMatches($sequence, $caseSensitive = true)
+ {
+ $portion = substr($this->data, $this->char, strlen($sequence));
+
+ return $caseSensitive ? $portion === $sequence : 0 === strcasecmp($portion, $sequence);
+ }
+
+ /**
+ * Get the current position.
+ *
+ * @return int The current intiger byte position.
+ */
+ public function position()
+ {
+ return $this->char;
+ }
+
+ /**
+ * Take a peek at the next character in the data.
+ *
+ * @return string The next character.
+ */
+ public function peek()
+ {
+ if (($this->char + 1) < $this->EOF) {
+ return $this->data[$this->char + 1];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the next character.
+ * Note: This advances the pointer.
+ *
+ * @return string The next character.
+ */
+ public function next()
+ {
+ ++$this->char;
+
+ if ($this->char < $this->EOF) {
+ return $this->data[$this->char];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the current character.
+ * Note, this does not advance the pointer.
+ *
+ * @return string The current character.
+ */
+ public function current()
+ {
+ if ($this->char < $this->EOF) {
+ return $this->data[$this->char];
+ }
+
+ return false;
+ }
+
+ /**
+ * Silently consume N chars.
+ *
+ * @param int $count
+ */
+ public function consume($count = 1)
+ {
+ $this->char += $count;
+ }
+
+ /**
+ * Unconsume some of the data.
+ * This moves the data pointer backwards.
+ *
+ * @param int $howMany The number of characters to move the pointer back.
+ */
+ public function unconsume($howMany = 1)
+ {
+ if (($this->char - $howMany) >= 0) {
+ $this->char -= $howMany;
+ }
+ }
+
+ /**
+ * Get the next group of that contains hex characters.
+ * Note, along with getting the characters the pointer in the data will be
+ * moved as well.
+ *
+ * @return string The next group that is hex characters.
+ */
+ public function getHex()
+ {
+ return $this->doCharsWhile(static::CHARS_HEX);
+ }
+
+ /**
+ * Get the next group of characters that are ASCII Alpha characters.
+ * Note, along with getting the characters the pointer in the data will be
+ * moved as well.
+ *
+ * @return string The next group of ASCII alpha characters.
+ */
+ public function getAsciiAlpha()
+ {
+ return $this->doCharsWhile(static::CHARS_ALPHA);
+ }
+
+ /**
+ * Get the next group of characters that are ASCII Alpha characters and numbers.
+ * Note, along with getting the characters the pointer in the data will be
+ * moved as well.
+ *
+ * @return string The next group of ASCII alpha characters and numbers.
+ */
+ public function getAsciiAlphaNum()
+ {
+ return $this->doCharsWhile(static::CHARS_ALNUM);
+ }
+
+ /**
+ * Get the next group of numbers.
+ * Note, along with getting the characters the pointer in the data will be
+ * moved as well.
+ *
+ * @return string The next group of numbers.
+ */
+ public function getNumeric()
+ {
+ return $this->doCharsWhile('0123456789');
+ }
+
+ /**
+ * Consume whitespace.
+ * Whitespace in HTML5 is: formfeed, tab, newline, space.
+ *
+ * @return int The length of the matched whitespaces.
+ */
+ public function whitespace()
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ $len = strspn($this->data, "\n\t\f ", $this->char);
+
+ $this->char += $len;
+
+ return $len;
+ }
+
+ /**
+ * Returns the current line that is being consumed.
+ *
+ * @return int The current line number.
+ */
+ public function currentLine()
+ {
+ if (empty($this->EOF) || 0 === $this->char) {
+ return 1;
+ }
+
+ // Add one to $this->char because we want the number for the next
+ // byte to be processed.
+ return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1;
+ }
+
+ /**
+ * Read chars until something in the mask is encountered.
+ *
+ * @param string $mask
+ *
+ * @return mixed
+ */
+ public function charsUntil($mask)
+ {
+ return $this->doCharsUntil($mask);
+ }
+
+ /**
+ * Read chars as long as the mask matches.
+ *
+ * @param string $mask
+ *
+ * @return int
+ */
+ public function charsWhile($mask)
+ {
+ return $this->doCharsWhile($mask);
+ }
+
+ /**
+ * Returns the current column of the current line that the tokenizer is at.
+ *
+ * Newlines are column 0. The first char after a newline is column 1.
+ *
+ * @return int The column number.
+ */
+ public function columnOffset()
+ {
+ // Short circuit for the first char.
+ if (0 === $this->char) {
+ return 0;
+ }
+
+ // strrpos is weird, and the offset needs to be negative for what we
+ // want (i.e., the last \n before $this->char). This needs to not have
+ // one (to make it point to the next character, the one we want the
+ // position of) added to it because strrpos's behaviour includes the
+ // final offset byte.
+ $backwardFrom = $this->char - 1 - strlen($this->data);
+ $lastLine = strrpos($this->data, "\n", $backwardFrom);
+
+ // However, for here we want the length up until the next byte to be
+ // processed, so add one to the current byte ($this->char).
+ if (false !== $lastLine) {
+ $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine);
+ } else {
+ // After a newline.
+ $findLengthOf = substr($this->data, 0, $this->char);
+ }
+
+ return UTF8Utils::countChars($findLengthOf);
+ }
+
+ /**
+ * Get all characters until EOF.
+ *
+ * This consumes characters until the EOF.
+ *
+ * @return int The number of characters remaining.
+ */
+ public function remainingChars()
+ {
+ if ($this->char < $this->EOF) {
+ $data = substr($this->data, $this->char);
+ $this->char = $this->EOF;
+
+ return $data;
+ }
+
+ return ''; // false;
+ }
+
+ /**
+ * Replace linefeed characters according to the spec.
+ *
+ * @param $data
+ *
+ * @return string
+ */
+ private function replaceLinefeeds($data)
+ {
+ /*
+ * U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED (LF) characters are treated specially.
+ * Any CR characters that are followed by LF characters must be removed, and any CR characters not
+ * followed by LF characters must be converted to LF characters. Thus, newlines in HTML DOMs are
+ * represented by LF characters, and there are never any CR characters in the input to the tokenization
+ * stage.
+ */
+ $crlfTable = array(
+ "\0" => "\xEF\xBF\xBD",
+ "\r\n" => "\n",
+ "\r" => "\n",
+ );
+
+ return strtr($data, $crlfTable);
+ }
+
+ /**
+ * Read to a particular match (or until $max bytes are consumed).
+ *
+ * This operates on byte sequences, not characters.
+ *
+ * Matches as far as possible until we reach a certain set of bytes
+ * and returns the matched substring.
+ *
+ * @param string $bytes Bytes to match.
+ * @param int $max Maximum number of bytes to scan.
+ *
+ * @return mixed Index or false if no match is found. You should use strong
+ * equality when checking the result, since index could be 0.
+ */
+ private function doCharsUntil($bytes, $max = null)
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ if (0 === $max || $max) {
+ $len = strcspn($this->data, $bytes, $this->char, $max);
+ } else {
+ $len = strcspn($this->data, $bytes, $this->char);
+ }
+
+ $string = (string) substr($this->data, $this->char, $len);
+ $this->char += $len;
+
+ return $string;
+ }
+
+ /**
+ * Returns the string so long as $bytes matches.
+ *
+ * Matches as far as possible with a certain set of bytes
+ * and returns the matched substring.
+ *
+ * @param string $bytes A mask of bytes to match. If ANY byte in this mask matches the
+ * current char, the pointer advances and the char is part of the
+ * substring.
+ * @param int $max The max number of chars to read.
+ *
+ * @return string
+ */
+ private function doCharsWhile($bytes, $max = null)
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ if (0 === $max || $max) {
+ $len = strspn($this->data, $bytes, $this->char, $max);
+ } else {
+ $len = strspn($this->data, $bytes, $this->char);
+ }
+
+ $string = (string) substr($this->data, $this->char, $len);
+ $this->char += $len;
+
+ return $string;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/StringInputStream.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/StringInputStream.php
new file mode 100644
index 000000000..0c213feb6
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/StringInputStream.php
@@ -0,0 +1,331 @@
+
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+// Some conventions:
+// - /* */ indicates verbatim text from the HTML 5 specification
+// MPB: Not sure which version of the spec. Moving from HTML5lib to
+// HTML5-PHP, I have been using this version:
+// http://www.w3.org/TR/2012/CR-html5-20121217/Overview.html#contents
+//
+// - // indicates regular comments
+
+/**
+ * @deprecated since 2.4, to remove in 3.0. Use a string in the scanner instead.
+ */
+class StringInputStream implements InputStream
+{
+ /**
+ * The string data we're parsing.
+ */
+ private $data;
+
+ /**
+ * The current integer byte position we are in $data.
+ */
+ private $char;
+
+ /**
+ * Length of $data; when $char === $data, we are at the end-of-file.
+ */
+ private $EOF;
+
+ /**
+ * Parse errors.
+ */
+ public $errors = array();
+
+ /**
+ * Create a new InputStream wrapper.
+ *
+ * @param string $data Data to parse.
+ * @param string $encoding The encoding to use for the data.
+ * @param string $debug A fprintf format to use to echo the data on stdout.
+ */
+ public function __construct($data, $encoding = 'UTF-8', $debug = '')
+ {
+ $data = UTF8Utils::convertToUTF8($data, $encoding);
+ if ($debug) {
+ fprintf(STDOUT, $debug, $data, strlen($data));
+ }
+
+ // There is good reason to question whether it makes sense to
+ // do this here, since most of these checks are done during
+ // parsing, and since this check doesn't actually *do* anything.
+ $this->errors = UTF8Utils::checkForIllegalCodepoints($data);
+
+ $data = $this->replaceLinefeeds($data);
+
+ $this->data = $data;
+ $this->char = 0;
+ $this->EOF = strlen($data);
+ }
+
+ public function __toString()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Replace linefeed characters according to the spec.
+ */
+ protected function replaceLinefeeds($data)
+ {
+ /*
+ * U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED (LF) characters are treated specially.
+ * Any CR characters that are followed by LF characters must be removed, and any CR characters not
+ * followed by LF characters must be converted to LF characters. Thus, newlines in HTML DOMs are
+ * represented by LF characters, and there are never any CR characters in the input to the tokenization
+ * stage.
+ */
+ $crlfTable = array(
+ "\0" => "\xEF\xBF\xBD",
+ "\r\n" => "\n",
+ "\r" => "\n",
+ );
+
+ return strtr($data, $crlfTable);
+ }
+
+ /**
+ * Returns the current line that the tokenizer is at.
+ */
+ public function currentLine()
+ {
+ if (empty($this->EOF) || 0 === $this->char) {
+ return 1;
+ }
+ // Add one to $this->char because we want the number for the next
+ // byte to be processed.
+ return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1;
+ }
+
+ /**
+ * @deprecated
+ */
+ public function getCurrentLine()
+ {
+ return $this->currentLine();
+ }
+
+ /**
+ * Returns the current column of the current line that the tokenizer is at.
+ * Newlines are column 0. The first char after a newline is column 1.
+ *
+ * @return int The column number.
+ */
+ public function columnOffset()
+ {
+ // Short circuit for the first char.
+ if (0 === $this->char) {
+ return 0;
+ }
+ // strrpos is weird, and the offset needs to be negative for what we
+ // want (i.e., the last \n before $this->char). This needs to not have
+ // one (to make it point to the next character, the one we want the
+ // position of) added to it because strrpos's behaviour includes the
+ // final offset byte.
+ $backwardFrom = $this->char - 1 - strlen($this->data);
+ $lastLine = strrpos($this->data, "\n", $backwardFrom);
+
+ // However, for here we want the length up until the next byte to be
+ // processed, so add one to the current byte ($this->char).
+ if (false !== $lastLine) {
+ $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine);
+ } else {
+ // After a newline.
+ $findLengthOf = substr($this->data, 0, $this->char);
+ }
+
+ return UTF8Utils::countChars($findLengthOf);
+ }
+
+ /**
+ * @deprecated
+ */
+ public function getColumnOffset()
+ {
+ return $this->columnOffset();
+ }
+
+ /**
+ * Get the current character.
+ *
+ * @return string The current character.
+ */
+ public function current()
+ {
+ return $this->data[$this->char];
+ }
+
+ /**
+ * Advance the pointer.
+ * This is part of the Iterator interface.
+ */
+ public function next()
+ {
+ ++$this->char;
+ }
+
+ /**
+ * Rewind to the start of the string.
+ */
+ public function rewind()
+ {
+ $this->char = 0;
+ }
+
+ /**
+ * Is the current pointer location valid.
+ *
+ * @return bool Whether the current pointer location is valid.
+ */
+ public function valid()
+ {
+ return $this->char < $this->EOF;
+ }
+
+ /**
+ * Get all characters until EOF.
+ *
+ * This reads to the end of the file, and sets the read marker at the
+ * end of the file.
+ *
+ * Note this performs bounds checking.
+ *
+ * @return string Returns the remaining text. If called when the InputStream is
+ * already exhausted, it returns an empty string.
+ */
+ public function remainingChars()
+ {
+ if ($this->char < $this->EOF) {
+ $data = substr($this->data, $this->char);
+ $this->char = $this->EOF;
+
+ return $data;
+ }
+
+ return ''; // false;
+ }
+
+ /**
+ * Read to a particular match (or until $max bytes are consumed).
+ *
+ * This operates on byte sequences, not characters.
+ *
+ * Matches as far as possible until we reach a certain set of bytes
+ * and returns the matched substring.
+ *
+ * @param string $bytes Bytes to match.
+ * @param int $max Maximum number of bytes to scan.
+ *
+ * @return mixed Index or false if no match is found. You should use strong
+ * equality when checking the result, since index could be 0.
+ */
+ public function charsUntil($bytes, $max = null)
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ if (0 === $max || $max) {
+ $len = strcspn($this->data, $bytes, $this->char, $max);
+ } else {
+ $len = strcspn($this->data, $bytes, $this->char);
+ }
+
+ $string = (string) substr($this->data, $this->char, $len);
+ $this->char += $len;
+
+ return $string;
+ }
+
+ /**
+ * Returns the string so long as $bytes matches.
+ *
+ * Matches as far as possible with a certain set of bytes
+ * and returns the matched substring.
+ *
+ * @param string $bytes A mask of bytes to match. If ANY byte in this mask matches the
+ * current char, the pointer advances and the char is part of the
+ * substring.
+ * @param int $max The max number of chars to read.
+ *
+ * @return string
+ */
+ public function charsWhile($bytes, $max = null)
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ if (0 === $max || $max) {
+ $len = strspn($this->data, $bytes, $this->char, $max);
+ } else {
+ $len = strspn($this->data, $bytes, $this->char);
+ }
+ $string = (string) substr($this->data, $this->char, $len);
+ $this->char += $len;
+
+ return $string;
+ }
+
+ /**
+ * Unconsume characters.
+ *
+ * @param int $howMany The number of characters to unconsume.
+ */
+ public function unconsume($howMany = 1)
+ {
+ if (($this->char - $howMany) >= 0) {
+ $this->char -= $howMany;
+ }
+ }
+
+ /**
+ * Look ahead without moving cursor.
+ */
+ public function peek()
+ {
+ if (($this->char + 1) <= $this->EOF) {
+ return $this->data[$this->char + 1];
+ }
+
+ return false;
+ }
+
+ public function key()
+ {
+ return $this->char;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/Tokenizer.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/Tokenizer.php
new file mode 100644
index 000000000..016919ae2
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/Tokenizer.php
@@ -0,0 +1,1197 @@
+scanner = $scanner;
+ $this->events = $eventHandler;
+ $this->mode = $mode;
+ }
+
+ /**
+ * Begin parsing.
+ *
+ * This will begin scanning the document, tokenizing as it goes.
+ * Tokens are emitted into the event handler.
+ *
+ * Tokenizing will continue until the document is completely
+ * read. Errors are emitted into the event handler, but
+ * the parser will attempt to continue parsing until the
+ * entire input stream is read.
+ */
+ public function parse()
+ {
+ do {
+ $this->consumeData();
+ // FIXME: Add infinite loop protection.
+ } while ($this->carryOn);
+ }
+
+ /**
+ * Set the text mode for the character data reader.
+ *
+ * HTML5 defines three different modes for reading text:
+ * - Normal: Read until a tag is encountered.
+ * - RCDATA: Read until a tag is encountered, but skip a few otherwise-
+ * special characters.
+ * - Raw: Read until a special closing tag is encountered (viz. pre, script)
+ *
+ * This allows those modes to be set.
+ *
+ * Normally, setting is done by the event handler via a special return code on
+ * startTag(), but it can also be set manually using this function.
+ *
+ * @param int $textmode One of Elements::TEXT_*.
+ * @param string $untilTag The tag that should stop RAW or RCDATA mode. Normal mode does not
+ * use this indicator.
+ */
+ public function setTextMode($textmode, $untilTag = null)
+ {
+ $this->textMode = $textmode & (Elements::TEXT_RAW | Elements::TEXT_RCDATA);
+ $this->untilTag = $untilTag;
+ }
+
+ /**
+ * Consume a character and make a move.
+ * HTML5 8.2.4.1.
+ */
+ protected function consumeData()
+ {
+ $tok = $this->scanner->current();
+
+ if ('&' === $tok) {
+ // Character reference
+ $ref = $this->decodeCharacterReference();
+ $this->buffer($ref);
+
+ $tok = $this->scanner->current();
+ }
+
+ // Parse tag
+ if ('<' === $tok) {
+ // Any buffered text data can go out now.
+ $this->flushBuffer();
+
+ $tok = $this->scanner->next();
+
+ if ('!' === $tok) {
+ $this->markupDeclaration();
+ } elseif ('/' === $tok) {
+ $this->endTag();
+ } elseif ('?' === $tok) {
+ $this->processingInstruction();
+ } elseif (ctype_alpha($tok)) {
+ $this->tagName();
+ } else {
+ $this->parseError('Illegal tag opening');
+ // TODO is this necessary ?
+ $this->characterData();
+ }
+
+ $tok = $this->scanner->current();
+ }
+
+ if (false === $tok) {
+ // Handle end of document
+ $this->eof();
+ } else {
+ // Parse character
+ switch ($this->textMode) {
+ case Elements::TEXT_RAW:
+ $this->rawText($tok);
+ break;
+
+ case Elements::TEXT_RCDATA:
+ $this->rcdata($tok);
+ break;
+
+ default:
+ if ('<' === $tok || '&' === $tok) {
+ break;
+ }
+
+ // NULL character
+ if ("\00" === $tok) {
+ $this->parseError('Received null character.');
+
+ $this->text .= $tok;
+ $this->scanner->consume();
+
+ break;
+ }
+
+ $this->text .= $this->scanner->charsUntil("<&\0");
+ }
+ }
+
+ return $this->carryOn;
+ }
+
+ /**
+ * Parse anything that looks like character data.
+ *
+ * Different rules apply based on the current text mode.
+ *
+ * @see Elements::TEXT_RAW Elements::TEXT_RCDATA.
+ */
+ protected function characterData()
+ {
+ $tok = $this->scanner->current();
+ if (false === $tok) {
+ return false;
+ }
+ switch ($this->textMode) {
+ case Elements::TEXT_RAW:
+ return $this->rawText($tok);
+ case Elements::TEXT_RCDATA:
+ return $this->rcdata($tok);
+ default:
+ if ('<' === $tok || '&' === $tok) {
+ return false;
+ }
+
+ return $this->text($tok);
+ }
+ }
+
+ /**
+ * This buffers the current token as character data.
+ *
+ * @param string $tok The current token.
+ *
+ * @return bool
+ */
+ protected function text($tok)
+ {
+ // This should never happen...
+ if (false === $tok) {
+ return false;
+ }
+
+ // NULL character
+ if ("\00" === $tok) {
+ $this->parseError('Received null character.');
+ }
+
+ $this->buffer($tok);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ /**
+ * Read text in RAW mode.
+ *
+ * @param string $tok The current token.
+ *
+ * @return bool
+ */
+ protected function rawText($tok)
+ {
+ if (is_null($this->untilTag)) {
+ return $this->text($tok);
+ }
+
+ $sequence = '' . $this->untilTag . '>';
+ $txt = $this->readUntilSequence($sequence);
+ $this->events->text($txt);
+ $this->setTextMode(0);
+
+ return $this->endTag();
+ }
+
+ /**
+ * Read text in RCDATA mode.
+ *
+ * @param string $tok The current token.
+ *
+ * @return bool
+ */
+ protected function rcdata($tok)
+ {
+ if (is_null($this->untilTag)) {
+ return $this->text($tok);
+ }
+
+ $sequence = '' . $this->untilTag;
+ $txt = '';
+
+ $caseSensitive = !Elements::isHtml5Element($this->untilTag);
+ while (false !== $tok && !('<' == $tok && ($this->scanner->sequenceMatches($sequence, $caseSensitive)))) {
+ if ('&' == $tok) {
+ $txt .= $this->decodeCharacterReference();
+ $tok = $this->scanner->current();
+ } else {
+ $txt .= $tok;
+ $tok = $this->scanner->next();
+ }
+ }
+ $len = strlen($sequence);
+ $this->scanner->consume($len);
+ $len += $this->scanner->whitespace();
+ if ('>' !== $this->scanner->current()) {
+ $this->parseError('Unclosed RCDATA end tag');
+ }
+
+ $this->scanner->unconsume($len);
+ $this->events->text($txt);
+ $this->setTextMode(0);
+
+ return $this->endTag();
+ }
+
+ /**
+ * If the document is read, emit an EOF event.
+ */
+ protected function eof()
+ {
+ // fprintf(STDOUT, "EOF");
+ $this->flushBuffer();
+ $this->events->eof();
+ $this->carryOn = false;
+ }
+
+ /**
+ * Look for markup.
+ */
+ protected function markupDeclaration()
+ {
+ $tok = $this->scanner->next();
+
+ // Comment:
+ if ('-' == $tok && '-' == $this->scanner->peek()) {
+ $this->scanner->consume(2);
+
+ return $this->comment();
+ } elseif ('D' == $tok || 'd' == $tok) { // Doctype
+ return $this->doctype();
+ } elseif ('[' == $tok) { // CDATA section
+ return $this->cdataSection();
+ }
+
+ // FINISH
+ $this->parseError('Expected . Emit an empty comment because 8.2.4.46 says to.
+ if ('>' == $tok) {
+ // Parse error. Emit the comment token.
+ $this->parseError("Expected comment data, got '>'");
+ $this->events->comment('');
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ // Replace NULL with the replacement char.
+ if ("\0" == $tok) {
+ $tok = UTF8Utils::FFFD;
+ }
+ while (!$this->isCommentEnd()) {
+ $comment .= $tok;
+ $tok = $this->scanner->next();
+ }
+
+ $this->events->comment($comment);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ /**
+ * Check if the scanner has reached the end of a comment.
+ *
+ * @return bool
+ */
+ protected function isCommentEnd()
+ {
+ $tok = $this->scanner->current();
+
+ // EOF
+ if (false === $tok) {
+ // Hit the end.
+ $this->parseError('Unexpected EOF in a comment.');
+
+ return true;
+ }
+
+ // If next two tokens are not '--', not the end.
+ if ('-' != $tok || '-' != $this->scanner->peek()) {
+ return false;
+ }
+
+ $this->scanner->consume(2); // Consume '-' and one of '!' or '>'
+
+ // Test for '>'
+ if ('>' == $this->scanner->current()) {
+ return true;
+ }
+ // Test for '!>'
+ if ('!' == $this->scanner->current() && '>' == $this->scanner->peek()) {
+ $this->scanner->consume(); // Consume the last '>'
+ return true;
+ }
+ // Unread '-' and one of '!' or '>';
+ $this->scanner->unconsume(2);
+
+ return false;
+ }
+
+ /**
+ * Parse a DOCTYPE.
+ *
+ * Parse a DOCTYPE declaration. This method has strong bearing on whether or
+ * not Quirksmode is enabled on the event handler.
+ *
+ * @todo This method is a little long. Should probably refactor.
+ *
+ * @return bool
+ */
+ protected function doctype()
+ {
+ // Check that string is DOCTYPE.
+ if ($this->scanner->sequenceMatches('DOCTYPE', false)) {
+ $this->scanner->consume(7);
+ } else {
+ $chars = $this->scanner->charsWhile('DOCTYPEdoctype');
+ $this->parseError('Expected DOCTYPE, got %s', $chars);
+
+ return $this->bogusComment('scanner->whitespace();
+ $tok = $this->scanner->current();
+
+ // EOF: die.
+ if (false === $tok) {
+ $this->events->doctype('html5', EventHandler::DOCTYPE_NONE, '', true);
+ $this->eof();
+
+ return true;
+ }
+
+ // NULL char: convert.
+ if ("\0" === $tok) {
+ $this->parseError('Unexpected null character in DOCTYPE.');
+ }
+
+ $stop = " \n\f>";
+ $doctypeName = $this->scanner->charsUntil($stop);
+ // Lowercase ASCII, replace \0 with FFFD
+ $doctypeName = strtolower(strtr($doctypeName, "\0", UTF8Utils::FFFD));
+
+ $tok = $this->scanner->current();
+
+ // If false, emit a parse error, DOCTYPE, and return.
+ if (false === $tok) {
+ $this->parseError('Unexpected EOF in DOCTYPE declaration.');
+ $this->events->doctype($doctypeName, EventHandler::DOCTYPE_NONE, null, true);
+
+ return true;
+ }
+
+ // Short DOCTYPE, like
+ if ('>' == $tok) {
+ // DOCTYPE without a name.
+ if (0 == strlen($doctypeName)) {
+ $this->parseError('Expected a DOCTYPE name. Got nothing.');
+ $this->events->doctype($doctypeName, 0, null, true);
+ $this->scanner->consume();
+
+ return true;
+ }
+ $this->events->doctype($doctypeName);
+ $this->scanner->consume();
+
+ return true;
+ }
+ $this->scanner->whitespace();
+
+ $pub = strtoupper($this->scanner->getAsciiAlpha());
+ $white = $this->scanner->whitespace();
+
+ // Get ID, and flag it as pub or system.
+ if (('PUBLIC' == $pub || 'SYSTEM' == $pub) && $white > 0) {
+ // Get the sys ID.
+ $type = 'PUBLIC' == $pub ? EventHandler::DOCTYPE_PUBLIC : EventHandler::DOCTYPE_SYSTEM;
+ $id = $this->quotedString("\0>");
+ if (false === $id) {
+ $this->events->doctype($doctypeName, $type, $pub, false);
+
+ return true;
+ }
+
+ // Premature EOF.
+ if (false === $this->scanner->current()) {
+ $this->parseError('Unexpected EOF in DOCTYPE');
+ $this->events->doctype($doctypeName, $type, $id, true);
+
+ return true;
+ }
+
+ // Well-formed complete DOCTYPE.
+ $this->scanner->whitespace();
+ if ('>' == $this->scanner->current()) {
+ $this->events->doctype($doctypeName, $type, $id, false);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ // If we get here, we have scanner->charsUntil('>');
+ $this->parseError('Malformed DOCTYPE.');
+ $this->events->doctype($doctypeName, $type, $id, true);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ // Else it's a bogus DOCTYPE.
+ // Consume to > and trash.
+ $this->scanner->charsUntil('>');
+
+ $this->parseError('Expected PUBLIC or SYSTEM. Got %s.', $pub);
+ $this->events->doctype($doctypeName, 0, null, true);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ /**
+ * Utility for reading a quoted string.
+ *
+ * @param string $stopchars Characters (in addition to a close-quote) that should stop the string.
+ * E.g. sometimes '>' is higher precedence than '"' or "'".
+ *
+ * @return mixed String if one is found (quotations omitted).
+ */
+ protected function quotedString($stopchars)
+ {
+ $tok = $this->scanner->current();
+ if ('"' == $tok || "'" == $tok) {
+ $this->scanner->consume();
+ $ret = $this->scanner->charsUntil($tok . $stopchars);
+ if ($this->scanner->current() == $tok) {
+ $this->scanner->consume();
+ } else {
+ // Parse error because no close quote.
+ $this->parseError('Expected %s, got %s', $tok, $this->scanner->current());
+ }
+
+ return $ret;
+ }
+
+ return false;
+ }
+
+ /**
+ * Handle a CDATA section.
+ *
+ * @return bool
+ */
+ protected function cdataSection()
+ {
+ $cdata = '';
+ $this->scanner->consume();
+
+ $chars = $this->scanner->charsWhile('CDAT');
+ if ('CDATA' != $chars || '[' != $this->scanner->current()) {
+ $this->parseError('Expected [CDATA[, got %s', $chars);
+
+ return $this->bogusComment('scanner->next();
+ do {
+ if (false === $tok) {
+ $this->parseError('Unexpected EOF inside CDATA.');
+ $this->bogusComment('scanner->next();
+ } while (!$this->scanner->sequenceMatches(']]>'));
+
+ // Consume ]]>
+ $this->scanner->consume(3);
+
+ $this->events->cdata($cdata);
+
+ return true;
+ }
+
+ // ================================================================
+ // Non-HTML5
+ // ================================================================
+
+ /**
+ * Handle a processing instruction.
+ *
+ * XML processing instructions are supposed to be ignored in HTML5,
+ * treated as "bogus comments". However, since we're not a user
+ * agent, we allow them. We consume until ?> and then issue a
+ * EventListener::processingInstruction() event.
+ *
+ * @return bool
+ */
+ protected function processingInstruction()
+ {
+ if ('?' != $this->scanner->current()) {
+ return false;
+ }
+
+ $tok = $this->scanner->next();
+ $procName = $this->scanner->getAsciiAlpha();
+ $white = $this->scanner->whitespace();
+
+ // If not a PI, send to bogusComment.
+ if (0 == strlen($procName) || 0 == $white || false == $this->scanner->current()) {
+ $this->parseError("Expected processing instruction name, got $tok");
+ $this->bogusComment('' . $tok . $procName);
+
+ return true;
+ }
+
+ $data = '';
+ // As long as it's not the case that the next two chars are ? and >.
+ while (!('?' == $this->scanner->current() && '>' == $this->scanner->peek())) {
+ $data .= $this->scanner->current();
+
+ $tok = $this->scanner->next();
+ if (false === $tok) {
+ $this->parseError('Unexpected EOF in processing instruction.');
+ $this->events->processingInstruction($procName, $data);
+
+ return true;
+ }
+ }
+
+ $this->scanner->consume(2); // Consume the closing tag
+ $this->events->processingInstruction($procName, $data);
+
+ return true;
+ }
+
+ // ================================================================
+ // UTILITY FUNCTIONS
+ // ================================================================
+
+ /**
+ * Read from the input stream until we get to the desired sequene
+ * or hit the end of the input stream.
+ *
+ * @param string $sequence
+ *
+ * @return string
+ */
+ protected function readUntilSequence($sequence)
+ {
+ $buffer = '';
+
+ // Optimization for reading larger blocks faster.
+ $first = substr($sequence, 0, 1);
+ while (false !== $this->scanner->current()) {
+ $buffer .= $this->scanner->charsUntil($first);
+
+ // Stop as soon as we hit the stopping condition.
+ if ($this->scanner->sequenceMatches($sequence, false)) {
+ return $buffer;
+ }
+ $buffer .= $this->scanner->current();
+ $this->scanner->consume();
+ }
+
+ // If we get here, we hit the EOF.
+ $this->parseError('Unexpected EOF during text read.');
+
+ return $buffer;
+ }
+
+ /**
+ * Check if upcomming chars match the given sequence.
+ *
+ * This will read the stream for the $sequence. If it's
+ * found, this will return true. If not, return false.
+ * Since this unconsumes any chars it reads, the caller
+ * will still need to read the next sequence, even if
+ * this returns true.
+ *
+ * Example: $this->scanner->sequenceMatches('') will
+ * see if the input stream is at the start of a
+ * '' string.
+ *
+ * @param string $sequence
+ * @param bool $caseSensitive
+ *
+ * @return bool
+ */
+ protected function sequenceMatches($sequence, $caseSensitive = true)
+ {
+ @trigger_error(__METHOD__ . ' method is deprecated since version 2.4 and will be removed in 3.0. Use Scanner::sequenceMatches() instead.', E_USER_DEPRECATED);
+
+ return $this->scanner->sequenceMatches($sequence, $caseSensitive);
+ }
+
+ /**
+ * Send a TEXT event with the contents of the text buffer.
+ *
+ * This emits an EventHandler::text() event with the current contents of the
+ * temporary text buffer. (The buffer is used to group as much PCDATA
+ * as we can instead of emitting lots and lots of TEXT events.)
+ */
+ protected function flushBuffer()
+ {
+ if ('' === $this->text) {
+ return;
+ }
+ $this->events->text($this->text);
+ $this->text = '';
+ }
+
+ /**
+ * Add text to the temporary buffer.
+ *
+ * @see flushBuffer()
+ *
+ * @param string $str
+ */
+ protected function buffer($str)
+ {
+ $this->text .= $str;
+ }
+
+ /**
+ * Emit a parse error.
+ *
+ * A parse error always returns false because it never consumes any
+ * characters.
+ *
+ * @param string $msg
+ *
+ * @return string
+ */
+ protected function parseError($msg)
+ {
+ $args = func_get_args();
+
+ if (count($args) > 1) {
+ array_shift($args);
+ $msg = vsprintf($msg, $args);
+ }
+
+ $line = $this->scanner->currentLine();
+ $col = $this->scanner->columnOffset();
+ $this->events->parseError($msg, $line, $col);
+
+ return false;
+ }
+
+ /**
+ * Decode a character reference and return the string.
+ *
+ * If $inAttribute is set to true, a bare & will be returned as-is.
+ *
+ * @param bool $inAttribute Set to true if the text is inside of an attribute value.
+ * false otherwise.
+ *
+ * @return string
+ */
+ protected function decodeCharacterReference($inAttribute = false)
+ {
+ // Next char after &.
+ $tok = $this->scanner->next();
+ $start = $this->scanner->position();
+
+ if (false === $tok) {
+ return '&';
+ }
+
+ // These indicate not an entity. We return just
+ // the &.
+ if ("\t" === $tok || "\n" === $tok || "\f" === $tok || ' ' === $tok || '&' === $tok || '<' === $tok) {
+ // $this->scanner->next();
+ return '&';
+ }
+
+ // Numeric entity
+ if ('#' === $tok) {
+ $tok = $this->scanner->next();
+
+ if (false === $tok) {
+ $this->parseError('Expected DEC; HEX;, got EOF');
+ $this->scanner->unconsume(1);
+
+ return '&';
+ }
+
+ // Hexidecimal encoding.
+ // X[0-9a-fA-F]+;
+ // x[0-9a-fA-F]+;
+ if ('x' === $tok || 'X' === $tok) {
+ $tok = $this->scanner->next(); // Consume x
+
+ // Convert from hex code to char.
+ $hex = $this->scanner->getHex();
+ if (empty($hex)) {
+ $this->parseError('Expected HEX;, got %s', $tok);
+ // We unconsume because we don't know what parser rules might
+ // be in effect for the remaining chars. For example. '>'
+ // might result in a specific parsing rule inside of tag
+ // contexts, while not inside of pcdata context.
+ $this->scanner->unconsume(2);
+
+ return '&';
+ }
+ $entity = CharacterReference::lookupHex($hex);
+ } // Decimal encoding.
+ // [0-9]+;
+ else {
+ // Convert from decimal to char.
+ $numeric = $this->scanner->getNumeric();
+ if (false === $numeric) {
+ $this->parseError('Expected DIGITS;, got %s', $tok);
+ $this->scanner->unconsume(2);
+
+ return '&';
+ }
+ $entity = CharacterReference::lookupDecimal($numeric);
+ }
+ } elseif ('=' === $tok && $inAttribute) {
+ return '&';
+ } else { // String entity.
+ // Attempt to consume a string up to a ';'.
+ // [a-zA-Z0-9]+;
+ $cname = $this->scanner->getAsciiAlphaNum();
+ $entity = CharacterReference::lookupName($cname);
+
+ // When no entity is found provide the name of the unmatched string
+ // and continue on as the & is not part of an entity. The & will
+ // be converted to & elsewhere.
+ if (null === $entity) {
+ if (!$inAttribute || '' === $cname) {
+ $this->parseError("No match in entity table for '%s'", $cname);
+ }
+ $this->scanner->unconsume($this->scanner->position() - $start);
+
+ return '&';
+ }
+ }
+
+ // The scanner has advanced the cursor for us.
+ $tok = $this->scanner->current();
+
+ // We have an entity. We're done here.
+ if (';' === $tok) {
+ $this->scanner->consume();
+
+ return $entity;
+ }
+
+ // Failing to match ; means unconsume the entire string.
+ $this->scanner->unconsume($this->scanner->position() - $start);
+
+ $this->parseError('Expected &ENTITY;, got &ENTITY%s (no trailing ;) ', $tok);
+
+ return '&';
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php
new file mode 100644
index 000000000..00d3951fd
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php
@@ -0,0 +1,127 @@
+ 1,
+ 'dd' => 1,
+ 'dt' => 1,
+ 'rt' => 1,
+ 'rp' => 1,
+ 'tr' => 1,
+ 'th' => 1,
+ 'td' => 1,
+ 'thead' => 1,
+ 'tfoot' => 1,
+ 'tbody' => 1,
+ 'table' => 1,
+ 'optgroup' => 1,
+ 'option' => 1,
+ );
+
+ /**
+ * Returns true if the given tagname has special processing rules.
+ */
+ public function hasRules($tagname)
+ {
+ return isset(static::$tags[$tagname]);
+ }
+
+ /**
+ * Evaluate the rule for the current tag name.
+ *
+ * This may modify the existing DOM.
+ *
+ * @return \DOMElement The new Current DOM element.
+ */
+ public function evaluate($new, $current)
+ {
+ switch ($new->tagName) {
+ case 'li':
+ return $this->handleLI($new, $current);
+ case 'dt':
+ case 'dd':
+ return $this->handleDT($new, $current);
+ case 'rt':
+ case 'rp':
+ return $this->handleRT($new, $current);
+ case 'optgroup':
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'optgroup',
+ ));
+ case 'option':
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'option',
+ ));
+ case 'tr':
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'tr',
+ ));
+ case 'td':
+ case 'th':
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'th',
+ 'td',
+ ));
+ case 'tbody':
+ case 'thead':
+ case 'tfoot':
+ case 'table': // Spec isn't explicit about this, but it's necessary.
+
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'thead',
+ 'tfoot',
+ 'tbody',
+ ));
+ }
+
+ return $current;
+ }
+
+ protected function handleLI($ele, $current)
+ {
+ return $this->closeIfCurrentMatches($ele, $current, array(
+ 'li',
+ ));
+ }
+
+ protected function handleDT($ele, $current)
+ {
+ return $this->closeIfCurrentMatches($ele, $current, array(
+ 'dt',
+ 'dd',
+ ));
+ }
+
+ protected function handleRT($ele, $current)
+ {
+ return $this->closeIfCurrentMatches($ele, $current, array(
+ 'rt',
+ 'rp',
+ ));
+ }
+
+ protected function closeIfCurrentMatches($ele, $current, $match)
+ {
+ if (in_array($current->tagName, $match, true)) {
+ $current->parentNode->appendChild($ele);
+ } else {
+ $current->appendChild($ele);
+ }
+
+ return $ele;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/UTF8Utils.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/UTF8Utils.php
new file mode 100644
index 000000000..f6a70bfac
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/UTF8Utils.php
@@ -0,0 +1,183 @@
+
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+use Masterminds\HTML5\Exception;
+
+class UTF8Utils
+{
+ /**
+ * The Unicode replacement character.
+ */
+ const FFFD = "\xEF\xBF\xBD";
+
+ /**
+ * Count the number of characters in a string.
+ * UTF-8 aware. This will try (in order) iconv, MB, libxml, and finally a custom counter.
+ *
+ * @param string $string
+ *
+ * @return int
+ */
+ public static function countChars($string)
+ {
+ // Get the length for the string we need.
+ if (function_exists('mb_strlen')) {
+ return mb_strlen($string, 'utf-8');
+ }
+
+ if (function_exists('iconv_strlen')) {
+ return iconv_strlen($string, 'utf-8');
+ }
+
+ if (function_exists('utf8_decode')) {
+ // MPB: Will this work? Won't certain decodes lead to two chars
+ // extrapolated out of 2-byte chars?
+ return strlen(utf8_decode($string));
+ }
+
+ $count = count_chars($string);
+
+ // 0x80 = 0x7F - 0 + 1 (one added to get inclusive range)
+ // 0x33 = 0xF4 - 0x2C + 1 (one added to get inclusive range)
+ return array_sum(array_slice($count, 0, 0x80)) + array_sum(array_slice($count, 0xC2, 0x33));
+ }
+
+ /**
+ * Convert data from the given encoding to UTF-8.
+ *
+ * This has not yet been tested with charactersets other than UTF-8.
+ * It should work with ISO-8859-1/-13 and standard Latin Win charsets.
+ *
+ * @param string $data The data to convert
+ * @param string $encoding A valid encoding. Examples: http://www.php.net/manual/en/mbstring.supported-encodings.php
+ *
+ * @return string
+ */
+ public static function convertToUTF8($data, $encoding = 'UTF-8')
+ {
+ /*
+ * From the HTML5 spec: Given an encoding, the bytes in the input stream must be converted
+ * to Unicode characters for the tokeniser, as described by the rules for that encoding,
+ * except that the leading U+FEFF BYTE ORDER MARK character, if any, must not be stripped
+ * by the encoding layer (it is stripped by the rule below). Bytes or sequences of bytes
+ * in the original byte stream that could not be converted to Unicode characters must be
+ * converted to U+FFFD REPLACEMENT CHARACTER code points.
+ */
+
+ // mb_convert_encoding is chosen over iconv because of a bug. The best
+ // details for the bug are on http://us1.php.net/manual/en/function.iconv.php#108643
+ // which contains links to the actual but reports as well as work around
+ // details.
+ if (function_exists('mb_convert_encoding')) {
+ // mb library has the following behaviors:
+ // - UTF-16 surrogates result in false.
+ // - Overlongs and outside Plane 16 result in empty strings.
+
+ // Before we run mb_convert_encoding we need to tell it what to do with
+ // characters it does not know. This could be different than the parent
+ // application executing this library so we store the value, change it
+ // to our needs, and then change it back when we are done. This feels
+ // a little excessive and it would be great if there was a better way.
+ $save = mb_substitute_character();
+ mb_substitute_character('none');
+ $data = mb_convert_encoding($data, 'UTF-8', $encoding);
+ mb_substitute_character($save);
+ }
+ // @todo Get iconv running in at least some environments if that is possible.
+ elseif (function_exists('iconv') && 'auto' !== $encoding) {
+ // fprintf(STDOUT, "iconv found\n");
+ // iconv has the following behaviors:
+ // - Overlong representations are ignored.
+ // - Beyond Plane 16 is replaced with a lower char.
+ // - Incomplete sequences generate a warning.
+ $data = @iconv($encoding, 'UTF-8//IGNORE', $data);
+ } else {
+ throw new Exception('Not implemented, please install mbstring or iconv');
+ }
+
+ /*
+ * One leading U+FEFF BYTE ORDER MARK character must be ignored if any are present.
+ */
+ if ("\xEF\xBB\xBF" === substr($data, 0, 3)) {
+ $data = substr($data, 3);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Checks for Unicode code points that are not valid in a document.
+ *
+ * @param string $data A string to analyze
+ *
+ * @return array An array of (string) error messages produced by the scanning
+ */
+ public static function checkForIllegalCodepoints($data)
+ {
+ // Vestigal error handling.
+ $errors = array();
+
+ /*
+ * All U+0000 null characters in the input must be replaced by U+FFFD REPLACEMENT CHARACTERs.
+ * Any occurrences of such characters is a parse error.
+ */
+ for ($i = 0, $count = substr_count($data, "\0"); $i < $count; ++$i) {
+ $errors[] = 'null-character';
+ }
+
+ /*
+ * Any occurrences of any characters in the ranges U+0001 to U+0008, U+000B, U+000E to U+001F, U+007F
+ * to U+009F, U+D800 to U+DFFF , U+FDD0 to U+FDEF, and characters U+FFFE, U+FFFF, U+1FFFE, U+1FFFF,
+ * U+2FFFE, U+2FFFF, U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE, U+6FFFF, U+7FFFE,
+ * U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF, U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF,
+ * U+DFFFE, U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, and U+10FFFF are parse errors.
+ * (These are all control characters or permanently undefined Unicode characters.)
+ */
+ // Check PCRE is loaded.
+ $count = preg_match_all(
+ '/(?:
+ [\x01-\x08\x0B\x0E-\x1F\x7F] # U+0001 to U+0008, U+000B, U+000E to U+001F and U+007F
+ |
+ \xC2[\x80-\x9F] # U+0080 to U+009F
+ |
+ \xED(?:\xA0[\x80-\xFF]|[\xA1-\xBE][\x00-\xFF]|\xBF[\x00-\xBF]) # U+D800 to U+DFFFF
+ |
+ \xEF\xB7[\x90-\xAF] # U+FDD0 to U+FDEF
+ |
+ \xEF\xBF[\xBE\xBF] # U+FFFE and U+FFFF
+ |
+ [\xF0-\xF4][\x8F-\xBF]\xBF[\xBE\xBF] # U+nFFFE and U+nFFFF (1 <= n <= 10_{16})
+ )/x', $data, $matches);
+ for ($i = 0; $i < $count; ++$i) {
+ $errors[] = 'invalid-codepoint';
+ }
+
+ return $errors;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php
new file mode 100644
index 000000000..e9421a12d
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php
@@ -0,0 +1,1533 @@
+ '	',
+ "\n" => '
',
+ '!' => '!',
+ '"' => '"',
+ '#' => '#',
+ '$' => '$',
+ '%' => '%',
+ '&' => '&',
+ '\'' => ''',
+ '(' => '(',
+ ')' => ')',
+ '*' => '*',
+ '+' => '+',
+ ',' => ',',
+ '.' => '.',
+ '/' => '/',
+ ':' => ':',
+ ';' => ';',
+ '<' => '<',
+ '<⃒' => '&nvlt',
+ '=' => '=',
+ '=⃥' => '&bne',
+ '>' => '>',
+ '>⃒' => '&nvgt',
+ '?' => '?',
+ '@' => '@',
+ '[' => '[',
+ '\\' => '\',
+ ']' => ']',
+ '^' => '^',
+ '_' => '_',
+ '`' => '`',
+ 'fj' => '&fjlig',
+ '{' => '{',
+ '|' => '|',
+ '}' => '}',
+ ' ' => ' ',
+ '¡' => '¡',
+ '¢' => '¢',
+ '£' => '£',
+ '¤' => '¤',
+ '¥' => '¥',
+ '¦' => '¦',
+ '§' => '§',
+ '¨' => '¨',
+ '©' => '©',
+ 'ª' => 'ª',
+ '«' => '«',
+ '¬' => '¬',
+ '' => '',
+ '®' => '®',
+ '¯' => '¯',
+ '°' => '°',
+ '±' => '±',
+ '²' => '²',
+ '³' => '³',
+ '´' => '´',
+ 'µ' => 'µ',
+ '¶' => '¶',
+ '·' => '·',
+ '¸' => '¸',
+ '¹' => '¹',
+ 'º' => 'º',
+ '»' => '»',
+ '¼' => '¼',
+ '½' => '½',
+ '¾' => '¾',
+ '¿' => '¿',
+ 'À' => 'À',
+ 'Á' => 'Á',
+ 'Â' => 'Â',
+ 'Ã' => 'Ã',
+ 'Ä' => 'Ä',
+ 'Å' => 'Å',
+ 'Æ' => 'Æ',
+ 'Ç' => 'Ç',
+ 'È' => 'È',
+ 'É' => 'É',
+ 'Ê' => 'Ê',
+ 'Ë' => 'Ë',
+ 'Ì' => 'Ì',
+ 'Í' => 'Í',
+ 'Î' => 'Î',
+ 'Ï' => 'Ï',
+ 'Ð' => 'Ð',
+ 'Ñ' => 'Ñ',
+ 'Ò' => 'Ò',
+ 'Ó' => 'Ó',
+ 'Ô' => 'Ô',
+ 'Õ' => 'Õ',
+ 'Ö' => 'Ö',
+ '×' => '×',
+ 'Ø' => 'Ø',
+ 'Ù' => 'Ù',
+ 'Ú' => 'Ú',
+ 'Û' => 'Û',
+ 'Ü' => 'Ü',
+ 'Ý' => 'Ý',
+ 'Þ' => 'Þ',
+ 'ß' => 'ß',
+ 'à' => 'à',
+ 'á' => 'á',
+ 'â' => 'â',
+ 'ã' => 'ã',
+ 'ä' => 'ä',
+ 'å' => 'å',
+ 'æ' => 'æ',
+ 'ç' => 'ç',
+ 'è' => 'è',
+ 'é' => 'é',
+ 'ê' => 'ê',
+ 'ë' => 'ë',
+ 'ì' => 'ì',
+ 'í' => 'í',
+ 'î' => 'î',
+ 'ï' => 'ï',
+ 'ð' => 'ð',
+ 'ñ' => 'ñ',
+ 'ò' => 'ò',
+ 'ó' => 'ó',
+ 'ô' => 'ô',
+ 'õ' => 'õ',
+ 'ö' => 'ö',
+ '÷' => '÷',
+ 'ø' => 'ø',
+ 'ù' => 'ù',
+ 'ú' => 'ú',
+ 'û' => 'û',
+ 'ü' => 'ü',
+ 'ý' => 'ý',
+ 'þ' => 'þ',
+ 'ÿ' => 'ÿ',
+ 'Ā' => 'Ā',
+ 'ā' => 'ā',
+ 'Ă' => 'Ă',
+ 'ă' => 'ă',
+ 'Ą' => 'Ą',
+ 'ą' => 'ą',
+ 'Ć' => 'Ć',
+ 'ć' => 'ć',
+ 'Ĉ' => 'Ĉ',
+ 'ĉ' => 'ĉ',
+ 'Ċ' => 'Ċ',
+ 'ċ' => 'ċ',
+ 'Č' => 'Č',
+ 'č' => 'č',
+ 'Ď' => 'Ď',
+ 'ď' => 'ď',
+ 'Đ' => 'Đ',
+ 'đ' => 'đ',
+ 'Ē' => 'Ē',
+ 'ē' => 'ē',
+ 'Ė' => 'Ė',
+ 'ė' => 'ė',
+ 'Ę' => 'Ę',
+ 'ę' => 'ę',
+ 'Ě' => 'Ě',
+ 'ě' => 'ě',
+ 'Ĝ' => 'Ĝ',
+ 'ĝ' => 'ĝ',
+ 'Ğ' => 'Ğ',
+ 'ğ' => 'ğ',
+ 'Ġ' => 'Ġ',
+ 'ġ' => 'ġ',
+ 'Ģ' => 'Ģ',
+ 'Ĥ' => 'Ĥ',
+ 'ĥ' => 'ĥ',
+ 'Ħ' => 'Ħ',
+ 'ħ' => 'ħ',
+ 'Ĩ' => 'Ĩ',
+ 'ĩ' => 'ĩ',
+ 'Ī' => 'Ī',
+ 'ī' => 'ī',
+ 'Į' => 'Į',
+ 'į' => 'į',
+ 'İ' => 'İ',
+ 'ı' => 'ı',
+ 'IJ' => 'IJ',
+ 'ij' => 'ij',
+ 'Ĵ' => 'Ĵ',
+ 'ĵ' => 'ĵ',
+ 'Ķ' => 'Ķ',
+ 'ķ' => 'ķ',
+ 'ĸ' => 'ĸ',
+ 'Ĺ' => 'Ĺ',
+ 'ĺ' => 'ĺ',
+ 'Ļ' => 'Ļ',
+ 'ļ' => 'ļ',
+ 'Ľ' => 'Ľ',
+ 'ľ' => 'ľ',
+ 'Ŀ' => 'Ŀ',
+ 'ŀ' => 'ŀ',
+ 'Ł' => 'Ł',
+ 'ł' => 'ł',
+ 'Ń' => 'Ń',
+ 'ń' => 'ń',
+ 'Ņ' => 'Ņ',
+ 'ņ' => 'ņ',
+ 'Ň' => 'Ň',
+ 'ň' => 'ň',
+ 'ʼn' => 'ʼn',
+ 'Ŋ' => 'Ŋ',
+ 'ŋ' => 'ŋ',
+ 'Ō' => 'Ō',
+ 'ō' => 'ō',
+ 'Ő' => 'Ő',
+ 'ő' => 'ő',
+ 'Œ' => 'Œ',
+ 'œ' => 'œ',
+ 'Ŕ' => 'Ŕ',
+ 'ŕ' => 'ŕ',
+ 'Ŗ' => 'Ŗ',
+ 'ŗ' => 'ŗ',
+ 'Ř' => 'Ř',
+ 'ř' => 'ř',
+ 'Ś' => 'Ś',
+ 'ś' => 'ś',
+ 'Ŝ' => 'Ŝ',
+ 'ŝ' => 'ŝ',
+ 'Ş' => 'Ş',
+ 'ş' => 'ş',
+ 'Š' => 'Š',
+ 'š' => 'š',
+ 'Ţ' => 'Ţ',
+ 'ţ' => 'ţ',
+ 'Ť' => 'Ť',
+ 'ť' => 'ť',
+ 'Ŧ' => 'Ŧ',
+ 'ŧ' => 'ŧ',
+ 'Ũ' => 'Ũ',
+ 'ũ' => 'ũ',
+ 'Ū' => 'Ū',
+ 'ū' => 'ū',
+ 'Ŭ' => 'Ŭ',
+ 'ŭ' => 'ŭ',
+ 'Ů' => 'Ů',
+ 'ů' => 'ů',
+ 'Ű' => 'Ű',
+ 'ű' => 'ű',
+ 'Ų' => 'Ų',
+ 'ų' => 'ų',
+ 'Ŵ' => 'Ŵ',
+ 'ŵ' => 'ŵ',
+ 'Ŷ' => 'Ŷ',
+ 'ŷ' => 'ŷ',
+ 'Ÿ' => 'Ÿ',
+ 'Ź' => 'Ź',
+ 'ź' => 'ź',
+ 'Ż' => 'Ż',
+ 'ż' => 'ż',
+ 'Ž' => 'Ž',
+ 'ž' => 'ž',
+ 'ƒ' => 'ƒ',
+ 'Ƶ' => 'Ƶ',
+ 'ǵ' => 'ǵ',
+ 'ȷ' => 'ȷ',
+ 'ˆ' => 'ˆ',
+ 'ˇ' => 'ˇ',
+ '˘' => '˘',
+ '˙' => '˙',
+ '˚' => '˚',
+ '˛' => '˛',
+ '˜' => '˜',
+ '˝' => '˝',
+ '̑' => '̑',
+ 'Α' => 'Α',
+ 'Β' => 'Β',
+ 'Γ' => 'Γ',
+ 'Δ' => 'Δ',
+ 'Ε' => 'Ε',
+ 'Ζ' => 'Ζ',
+ 'Η' => 'Η',
+ 'Θ' => 'Θ',
+ 'Ι' => 'Ι',
+ 'Κ' => 'Κ',
+ 'Λ' => 'Λ',
+ 'Μ' => 'Μ',
+ 'Ν' => 'Ν',
+ 'Ξ' => 'Ξ',
+ 'Ο' => 'Ο',
+ 'Π' => 'Π',
+ 'Ρ' => 'Ρ',
+ 'Σ' => 'Σ',
+ 'Τ' => 'Τ',
+ 'Υ' => 'Υ',
+ 'Φ' => 'Φ',
+ 'Χ' => 'Χ',
+ 'Ψ' => 'Ψ',
+ 'Ω' => 'Ω',
+ 'α' => 'α',
+ 'β' => 'β',
+ 'γ' => 'γ',
+ 'δ' => 'δ',
+ 'ε' => 'ε',
+ 'ζ' => 'ζ',
+ 'η' => 'η',
+ 'θ' => 'θ',
+ 'ι' => 'ι',
+ 'κ' => 'κ',
+ 'λ' => 'λ',
+ 'μ' => 'μ',
+ 'ν' => 'ν',
+ 'ξ' => 'ξ',
+ 'ο' => 'ο',
+ 'π' => 'π',
+ 'ρ' => 'ρ',
+ 'ς' => 'ς',
+ 'σ' => 'σ',
+ 'τ' => 'τ',
+ 'υ' => 'υ',
+ 'φ' => 'φ',
+ 'χ' => 'χ',
+ 'ψ' => 'ψ',
+ 'ω' => 'ω',
+ 'ϑ' => 'ϑ',
+ 'ϒ' => 'ϒ',
+ 'ϕ' => 'ϕ',
+ 'ϖ' => 'ϖ',
+ 'Ϝ' => 'Ϝ',
+ 'ϝ' => 'ϝ',
+ 'ϰ' => 'ϰ',
+ 'ϱ' => 'ϱ',
+ 'ϵ' => 'ϵ',
+ '϶' => '϶',
+ 'Ё' => 'Ё',
+ 'Ђ' => 'Ђ',
+ 'Ѓ' => 'Ѓ',
+ 'Є' => 'Є',
+ 'Ѕ' => 'Ѕ',
+ 'І' => 'І',
+ 'Ї' => 'Ї',
+ 'Ј' => 'Ј',
+ 'Љ' => 'Љ',
+ 'Њ' => 'Њ',
+ 'Ћ' => 'Ћ',
+ 'Ќ' => 'Ќ',
+ 'Ў' => 'Ў',
+ 'Џ' => 'Џ',
+ 'А' => 'А',
+ 'Б' => 'Б',
+ 'В' => 'В',
+ 'Г' => 'Г',
+ 'Д' => 'Д',
+ 'Е' => 'Е',
+ 'Ж' => 'Ж',
+ 'З' => 'З',
+ 'И' => 'И',
+ 'Й' => 'Й',
+ 'К' => 'К',
+ 'Л' => 'Л',
+ 'М' => 'М',
+ 'Н' => 'Н',
+ 'О' => 'О',
+ 'П' => 'П',
+ 'Р' => 'Р',
+ 'С' => 'С',
+ 'Т' => 'Т',
+ 'У' => 'У',
+ 'Ф' => 'Ф',
+ 'Х' => 'Х',
+ 'Ц' => 'Ц',
+ 'Ч' => 'Ч',
+ 'Ш' => 'Ш',
+ 'Щ' => 'Щ',
+ 'Ъ' => 'Ъ',
+ 'Ы' => 'Ы',
+ 'Ь' => 'Ь',
+ 'Э' => 'Э',
+ 'Ю' => 'Ю',
+ 'Я' => 'Я',
+ 'а' => 'а',
+ 'б' => 'б',
+ 'в' => 'в',
+ 'г' => 'г',
+ 'д' => 'д',
+ 'е' => 'е',
+ 'ж' => 'ж',
+ 'з' => 'з',
+ 'и' => 'и',
+ 'й' => 'й',
+ 'к' => 'к',
+ 'л' => 'л',
+ 'м' => 'м',
+ 'н' => 'н',
+ 'о' => 'о',
+ 'п' => 'п',
+ 'р' => 'р',
+ 'с' => 'с',
+ 'т' => 'т',
+ 'у' => 'у',
+ 'ф' => 'ф',
+ 'х' => 'х',
+ 'ц' => 'ц',
+ 'ч' => 'ч',
+ 'ш' => 'ш',
+ 'щ' => 'щ',
+ 'ъ' => 'ъ',
+ 'ы' => 'ы',
+ 'ь' => 'ь',
+ 'э' => 'э',
+ 'ю' => 'ю',
+ 'я' => 'я',
+ 'ё' => 'ё',
+ 'ђ' => 'ђ',
+ 'ѓ' => 'ѓ',
+ 'є' => 'є',
+ 'ѕ' => 'ѕ',
+ 'і' => 'і',
+ 'ї' => 'ї',
+ 'ј' => 'ј',
+ 'љ' => 'љ',
+ 'њ' => 'њ',
+ 'ћ' => 'ћ',
+ 'ќ' => 'ќ',
+ 'ў' => 'ў',
+ 'џ' => 'џ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ '' => '​',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '‐' => '‐',
+ '–' => '–',
+ '—' => '—',
+ '―' => '―',
+ '‖' => '‖',
+ '‘' => '‘',
+ '’' => '’',
+ '‚' => '‚',
+ '“' => '“',
+ '”' => '”',
+ '„' => '„',
+ '†' => '†',
+ '‡' => '‡',
+ '•' => '•',
+ '‥' => '‥',
+ '…' => '…',
+ '‰' => '‰',
+ '‱' => '‱',
+ '′' => '′',
+ '″' => '″',
+ '‴' => '‴',
+ '‵' => '‵',
+ '‹' => '‹',
+ '›' => '›',
+ '‾' => '‾',
+ '⁁' => '⁁',
+ '⁃' => '⁃',
+ '⁄' => '⁄',
+ '⁏' => '⁏',
+ '⁗' => '⁗',
+ ' ' => ' ',
+ ' ' => '&ThickSpace',
+ '' => '⁠',
+ '' => '⁡',
+ '' => '⁢',
+ '' => '⁣',
+ '€' => '€',
+ '⃛' => '⃛',
+ '⃜' => '⃜',
+ 'ℂ' => 'ℂ',
+ '℅' => '℅',
+ 'ℊ' => 'ℊ',
+ 'ℋ' => 'ℋ',
+ 'ℌ' => 'ℌ',
+ 'ℍ' => 'ℍ',
+ 'ℎ' => 'ℎ',
+ 'ℏ' => 'ℏ',
+ 'ℐ' => 'ℐ',
+ 'ℑ' => 'ℑ',
+ 'ℒ' => 'ℒ',
+ 'ℓ' => 'ℓ',
+ 'ℕ' => 'ℕ',
+ '№' => '№',
+ '℗' => '℗',
+ '℘' => '℘',
+ 'ℙ' => 'ℙ',
+ 'ℚ' => 'ℚ',
+ 'ℛ' => 'ℛ',
+ 'ℜ' => 'ℜ',
+ 'ℝ' => 'ℝ',
+ '℞' => '℞',
+ '™' => '™',
+ 'ℤ' => 'ℤ',
+ '℧' => '℧',
+ 'ℨ' => 'ℨ',
+ '℩' => '℩',
+ 'ℬ' => 'ℬ',
+ 'ℭ' => 'ℭ',
+ 'ℯ' => 'ℯ',
+ 'ℰ' => 'ℰ',
+ 'ℱ' => 'ℱ',
+ 'ℳ' => 'ℳ',
+ 'ℴ' => 'ℴ',
+ 'ℵ' => 'ℵ',
+ 'ℶ' => 'ℶ',
+ 'ℷ' => 'ℷ',
+ 'ℸ' => 'ℸ',
+ 'ⅅ' => 'ⅅ',
+ 'ⅆ' => 'ⅆ',
+ 'ⅇ' => 'ⅇ',
+ 'ⅈ' => 'ⅈ',
+ '⅓' => '⅓',
+ '⅔' => '⅔',
+ '⅕' => '⅕',
+ '⅖' => '⅖',
+ '⅗' => '⅗',
+ '⅘' => '⅘',
+ '⅙' => '⅙',
+ '⅚' => '⅚',
+ '⅛' => '⅛',
+ '⅜' => '⅜',
+ '⅝' => '⅝',
+ '⅞' => '⅞',
+ '←' => '←',
+ '↑' => '↑',
+ '→' => '→',
+ '↓' => '↓',
+ '↔' => '↔',
+ '↕' => '↕',
+ '↖' => '↖',
+ '↗' => '↗',
+ '↘' => '↘',
+ '↙' => '↙',
+ '↚' => '↚',
+ '↛' => '↛',
+ '↝' => '↝',
+ '↝̸' => '&nrarrw',
+ '↞' => '↞',
+ '↟' => '↟',
+ '↠' => '↠',
+ '↡' => '↡',
+ '↢' => '↢',
+ '↣' => '↣',
+ '↤' => '↤',
+ '↥' => '↥',
+ '↦' => '↦',
+ '↧' => '↧',
+ '↩' => '↩',
+ '↪' => '↪',
+ '↫' => '↫',
+ '↬' => '↬',
+ '↭' => '↭',
+ '↮' => '↮',
+ '↰' => '↰',
+ '↱' => '↱',
+ '↲' => '↲',
+ '↳' => '↳',
+ '↵' => '↵',
+ '↶' => '↶',
+ '↷' => '↷',
+ '↺' => '↺',
+ '↻' => '↻',
+ '↼' => '↼',
+ '↽' => '↽',
+ '↾' => '↾',
+ '↿' => '↿',
+ '⇀' => '⇀',
+ '⇁' => '⇁',
+ '⇂' => '⇂',
+ '⇃' => '⇃',
+ '⇄' => '⇄',
+ '⇅' => '⇅',
+ '⇆' => '⇆',
+ '⇇' => '⇇',
+ '⇈' => '⇈',
+ '⇉' => '⇉',
+ '⇊' => '⇊',
+ '⇋' => '⇋',
+ '⇌' => '⇌',
+ '⇍' => '⇍',
+ '⇎' => '⇎',
+ '⇏' => '⇏',
+ '⇐' => '⇐',
+ '⇑' => '⇑',
+ '⇒' => '⇒',
+ '⇓' => '⇓',
+ '⇔' => '⇔',
+ '⇕' => '⇕',
+ '⇖' => '⇖',
+ '⇗' => '⇗',
+ '⇘' => '⇘',
+ '⇙' => '⇙',
+ '⇚' => '⇚',
+ '⇛' => '⇛',
+ '⇝' => '⇝',
+ '⇤' => '⇤',
+ '⇥' => '⇥',
+ '⇵' => '⇵',
+ '⇽' => '⇽',
+ '⇾' => '⇾',
+ '⇿' => '⇿',
+ '∀' => '∀',
+ '∁' => '∁',
+ '∂' => '∂',
+ '∂̸' => '&npart',
+ '∃' => '∃',
+ '∄' => '∄',
+ '∅' => '∅',
+ '∇' => '∇',
+ '∈' => '∈',
+ '∉' => '∉',
+ '∋' => '∋',
+ '∌' => '∌',
+ '∏' => '∏',
+ '∐' => '∐',
+ '∑' => '∑',
+ '−' => '−',
+ '∓' => '∓',
+ '∔' => '∔',
+ '∖' => '∖',
+ '∗' => '∗',
+ '∘' => '∘',
+ '√' => '√',
+ '∝' => '∝',
+ '∞' => '∞',
+ '∟' => '∟',
+ '∠' => '∠',
+ '∠⃒' => '&nang',
+ '∡' => '∡',
+ '∢' => '∢',
+ '∣' => '∣',
+ '∤' => '∤',
+ '∥' => '∥',
+ '∦' => '∦',
+ '∧' => '∧',
+ '∨' => '∨',
+ '∩' => '∩',
+ '∩︀' => '&caps',
+ '∪' => '∪',
+ '∪︀' => '&cups',
+ '∫' => '∫',
+ '∬' => '∬',
+ '∭' => '∭',
+ '∮' => '∮',
+ '∯' => '∯',
+ '∰' => '∰',
+ '∱' => '∱',
+ '∲' => '∲',
+ '∳' => '∳',
+ '∴' => '∴',
+ '∵' => '∵',
+ '∶' => '∶',
+ '∷' => '∷',
+ '∸' => '∸',
+ '∺' => '∺',
+ '∻' => '∻',
+ '∼' => '∼',
+ '∼⃒' => '&nvsim',
+ '∽' => '∽',
+ '∽̱' => '&race',
+ '∾' => '∾',
+ '∾̳' => '&acE',
+ '∿' => '∿',
+ '≀' => '≀',
+ '≁' => '≁',
+ '≂' => '≂',
+ '≂̸' => '&nesim',
+ '≃' => '≃',
+ '≄' => '≄',
+ '≅' => '≅',
+ '≆' => '≆',
+ '≇' => '≇',
+ '≈' => '≈',
+ '≉' => '≉',
+ '≊' => '≊',
+ '≋' => '≋',
+ '≋̸' => '&napid',
+ '≌' => '≌',
+ '≍' => '≍',
+ '≍⃒' => '&nvap',
+ '≎' => '≎',
+ '≎̸' => '&nbump',
+ '≏' => '≏',
+ '≏̸' => '&nbumpe',
+ '≐' => '≐',
+ '≐̸' => '&nedot',
+ '≑' => '≑',
+ '≒' => '≒',
+ '≓' => '≓',
+ '≔' => '≔',
+ '≕' => '≕',
+ '≖' => '≖',
+ '≗' => '≗',
+ '≙' => '≙',
+ '≚' => '≚',
+ '≜' => '≜',
+ '≟' => '≟',
+ '≠' => '≠',
+ '≡' => '≡',
+ '≡⃥' => '&bnequiv',
+ '≢' => '≢',
+ '≤' => '≤',
+ '≤⃒' => '&nvle',
+ '≥' => '≥',
+ '≥⃒' => '&nvge',
+ '≦' => '≦',
+ '≦̸' => '&nlE',
+ '≧' => '≧',
+ '≧̸' => '&NotGreaterFullEqual',
+ '≨' => '≨',
+ '≨︀' => '&lvertneqq',
+ '≩' => '≩',
+ '≩︀' => '&gvertneqq',
+ '≪' => '≪',
+ '≪̸' => '&nLtv',
+ '≪⃒' => '&nLt',
+ '≫' => '≫',
+ '≫̸' => '&NotGreaterGreater',
+ '≫⃒' => '&nGt',
+ '≬' => '≬',
+ '≭' => '≭',
+ '≮' => '≮',
+ '≯' => '≯',
+ '≰' => '≰',
+ '≱' => '≱',
+ '≲' => '≲',
+ '≳' => '≳',
+ '≴' => '≴',
+ '≵' => '≵',
+ '≶' => '≶',
+ '≷' => '≷',
+ '≸' => '≸',
+ '≹' => '≹',
+ '≺' => '≺',
+ '≻' => '≻',
+ '≼' => '≼',
+ '≽' => '≽',
+ '≾' => '≾',
+ '≿' => '≿',
+ '≿̸' => '&NotSucceedsTilde',
+ '⊀' => '⊀',
+ '⊁' => '⊁',
+ '⊂' => '⊂',
+ '⊂⃒' => '&vnsub',
+ '⊃' => '⊃',
+ '⊃⃒' => '&nsupset',
+ '⊄' => '⊄',
+ '⊅' => '⊅',
+ '⊆' => '⊆',
+ '⊇' => '⊇',
+ '⊈' => '⊈',
+ '⊉' => '⊉',
+ '⊊' => '⊊',
+ '⊊︀' => '&vsubne',
+ '⊋' => '⊋',
+ '⊋︀' => '&vsupne',
+ '⊍' => '⊍',
+ '⊎' => '⊎',
+ '⊏' => '⊏',
+ '⊏̸' => '&NotSquareSubset',
+ '⊐' => '⊐',
+ '⊐̸' => '&NotSquareSuperset',
+ '⊑' => '⊑',
+ '⊒' => '⊒',
+ '⊓' => '⊓',
+ '⊓︀' => '&sqcaps',
+ '⊔' => '⊔',
+ '⊔︀' => '&sqcups',
+ '⊕' => '⊕',
+ '⊖' => '⊖',
+ '⊗' => '⊗',
+ '⊘' => '⊘',
+ '⊙' => '⊙',
+ '⊚' => '⊚',
+ '⊛' => '⊛',
+ '⊝' => '⊝',
+ '⊞' => '⊞',
+ '⊟' => '⊟',
+ '⊠' => '⊠',
+ '⊡' => '⊡',
+ '⊢' => '⊢',
+ '⊣' => '⊣',
+ '⊤' => '⊤',
+ '⊥' => '⊥',
+ '⊧' => '⊧',
+ '⊨' => '⊨',
+ '⊩' => '⊩',
+ '⊪' => '⊪',
+ '⊫' => '⊫',
+ '⊬' => '⊬',
+ '⊭' => '⊭',
+ '⊮' => '⊮',
+ '⊯' => '⊯',
+ '⊰' => '⊰',
+ '⊲' => '⊲',
+ '⊳' => '⊳',
+ '⊴' => '⊴',
+ '⊴⃒' => '&nvltrie',
+ '⊵' => '⊵',
+ '⊵⃒' => '&nvrtrie',
+ '⊶' => '⊶',
+ '⊷' => '⊷',
+ '⊸' => '⊸',
+ '⊹' => '⊹',
+ '⊺' => '⊺',
+ '⊻' => '⊻',
+ '⊽' => '⊽',
+ '⊾' => '⊾',
+ '⊿' => '⊿',
+ '⋀' => '⋀',
+ '⋁' => '⋁',
+ '⋂' => '⋂',
+ '⋃' => '⋃',
+ '⋄' => '⋄',
+ '⋅' => '⋅',
+ '⋆' => '⋆',
+ '⋇' => '⋇',
+ '⋈' => '⋈',
+ '⋉' => '⋉',
+ '⋊' => '⋊',
+ '⋋' => '⋋',
+ '⋌' => '⋌',
+ '⋍' => '⋍',
+ '⋎' => '⋎',
+ '⋏' => '⋏',
+ '⋐' => '⋐',
+ '⋑' => '⋑',
+ '⋒' => '⋒',
+ '⋓' => '⋓',
+ '⋔' => '⋔',
+ '⋕' => '⋕',
+ '⋖' => '⋖',
+ '⋗' => '⋗',
+ '⋘' => '⋘',
+ '⋘̸' => '&nLl',
+ '⋙' => '⋙',
+ '⋙̸' => '&nGg',
+ '⋚' => '⋚',
+ '⋚︀' => '&lesg',
+ '⋛' => '⋛',
+ '⋛︀' => '&gesl',
+ '⋞' => '⋞',
+ '⋟' => '⋟',
+ '⋠' => '⋠',
+ '⋡' => '⋡',
+ '⋢' => '⋢',
+ '⋣' => '⋣',
+ '⋦' => '⋦',
+ '⋧' => '⋧',
+ '⋨' => '⋨',
+ '⋩' => '⋩',
+ '⋪' => '⋪',
+ '⋫' => '⋫',
+ '⋬' => '⋬',
+ '⋭' => '⋭',
+ '⋮' => '⋮',
+ '⋯' => '⋯',
+ '⋰' => '⋰',
+ '⋱' => '⋱',
+ '⋲' => '⋲',
+ '⋳' => '⋳',
+ '⋴' => '⋴',
+ '⋵' => '⋵',
+ '⋵̸' => '¬indot',
+ '⋶' => '⋶',
+ '⋷' => '⋷',
+ '⋹' => '⋹',
+ '⋹̸' => '¬inE',
+ '⋺' => '⋺',
+ '⋻' => '⋻',
+ '⋼' => '⋼',
+ '⋽' => '⋽',
+ '⋾' => '⋾',
+ '⌅' => '⌅',
+ '⌆' => '⌆',
+ '⌈' => '⌈',
+ '⌉' => '⌉',
+ '⌊' => '⌊',
+ '⌋' => '⌋',
+ '⌌' => '⌌',
+ '⌍' => '⌍',
+ '⌎' => '⌎',
+ '⌏' => '⌏',
+ '⌐' => '⌐',
+ '⌒' => '⌒',
+ '⌓' => '⌓',
+ '⌕' => '⌕',
+ '⌖' => '⌖',
+ '⌜' => '⌜',
+ '⌝' => '⌝',
+ '⌞' => '⌞',
+ '⌟' => '⌟',
+ '⌢' => '⌢',
+ '⌣' => '⌣',
+ '⌭' => '⌭',
+ '⌮' => '⌮',
+ '⌶' => '⌶',
+ '⌽' => '⌽',
+ '⌿' => '⌿',
+ '⍼' => '⍼',
+ '⎰' => '⎰',
+ '⎱' => '⎱',
+ '⎴' => '⎴',
+ '⎵' => '⎵',
+ '⎶' => '⎶',
+ '⏜' => '⏜',
+ '⏝' => '⏝',
+ '⏞' => '⏞',
+ '⏟' => '⏟',
+ '⏢' => '⏢',
+ '⏧' => '⏧',
+ '␣' => '␣',
+ 'Ⓢ' => 'Ⓢ',
+ '─' => '─',
+ '│' => '│',
+ '┌' => '┌',
+ '┐' => '┐',
+ '└' => '└',
+ '┘' => '┘',
+ '├' => '├',
+ '┤' => '┤',
+ '┬' => '┬',
+ '┴' => '┴',
+ '┼' => '┼',
+ '═' => '═',
+ '║' => '║',
+ '╒' => '╒',
+ '╓' => '╓',
+ '╔' => '╔',
+ '╕' => '╕',
+ '╖' => '╖',
+ '╗' => '╗',
+ '╘' => '╘',
+ '╙' => '╙',
+ '╚' => '╚',
+ '╛' => '╛',
+ '╜' => '╜',
+ '╝' => '╝',
+ '╞' => '╞',
+ '╟' => '╟',
+ '╠' => '╠',
+ '╡' => '╡',
+ '╢' => '╢',
+ '╣' => '╣',
+ '╤' => '╤',
+ '╥' => '╥',
+ '╦' => '╦',
+ '╧' => '╧',
+ '╨' => '╨',
+ '╩' => '╩',
+ '╪' => '╪',
+ '╫' => '╫',
+ '╬' => '╬',
+ '▀' => '▀',
+ '▄' => '▄',
+ '█' => '█',
+ '░' => '░',
+ '▒' => '▒',
+ '▓' => '▓',
+ '□' => '□',
+ '▪' => '▪',
+ '▫' => '▫',
+ '▭' => '▭',
+ '▮' => '▮',
+ '▱' => '▱',
+ '△' => '△',
+ '▴' => '▴',
+ '▵' => '▵',
+ '▸' => '▸',
+ '▹' => '▹',
+ '▽' => '▽',
+ '▾' => '▾',
+ '▿' => '▿',
+ '◂' => '◂',
+ '◃' => '◃',
+ '◊' => '◊',
+ '○' => '○',
+ '◬' => '◬',
+ '◯' => '◯',
+ '◸' => '◸',
+ '◹' => '◹',
+ '◺' => '◺',
+ '◻' => '◻',
+ '◼' => '◼',
+ '★' => '★',
+ '☆' => '☆',
+ '☎' => '☎',
+ '♀' => '♀',
+ '♂' => '♂',
+ '♠' => '♠',
+ '♣' => '♣',
+ '♥' => '♥',
+ '♦' => '♦',
+ '♪' => '♪',
+ '♭' => '♭',
+ '♮' => '♮',
+ '♯' => '♯',
+ '✓' => '✓',
+ '✗' => '✗',
+ '✠' => '✠',
+ '✶' => '✶',
+ '❘' => '❘',
+ '❲' => '❲',
+ '❳' => '❳',
+ '⟈' => '⟈',
+ '⟉' => '⟉',
+ '⟦' => '⟦',
+ '⟧' => '⟧',
+ '⟨' => '⟨',
+ '⟩' => '⟩',
+ '⟪' => '⟪',
+ '⟫' => '⟫',
+ '⟬' => '⟬',
+ '⟭' => '⟭',
+ '⟵' => '⟵',
+ '⟶' => '⟶',
+ '⟷' => '⟷',
+ '⟸' => '⟸',
+ '⟹' => '⟹',
+ '⟺' => '⟺',
+ '⟼' => '⟼',
+ '⟿' => '⟿',
+ '⤂' => '⤂',
+ '⤃' => '⤃',
+ '⤄' => '⤄',
+ '⤅' => '⤅',
+ '⤌' => '⤌',
+ '⤍' => '⤍',
+ '⤎' => '⤎',
+ '⤏' => '⤏',
+ '⤐' => '⤐',
+ '⤑' => '⤑',
+ '⤒' => '⤒',
+ '⤓' => '⤓',
+ '⤖' => '⤖',
+ '⤙' => '⤙',
+ '⤚' => '⤚',
+ '⤛' => '⤛',
+ '⤜' => '⤜',
+ '⤝' => '⤝',
+ '⤞' => '⤞',
+ '⤟' => '⤟',
+ '⤠' => '⤠',
+ '⤣' => '⤣',
+ '⤤' => '⤤',
+ '⤥' => '⤥',
+ '⤦' => '⤦',
+ '⤧' => '⤧',
+ '⤨' => '⤨',
+ '⤩' => '⤩',
+ '⤪' => '⤪',
+ '⤳' => '⤳',
+ '⤳̸' => '&nrarrc',
+ '⤵' => '⤵',
+ '⤶' => '⤶',
+ '⤷' => '⤷',
+ '⤸' => '⤸',
+ '⤹' => '⤹',
+ '⤼' => '⤼',
+ '⤽' => '⤽',
+ '⥅' => '⥅',
+ '⥈' => '⥈',
+ '⥉' => '⥉',
+ '⥊' => '⥊',
+ '⥋' => '⥋',
+ '⥎' => '⥎',
+ '⥏' => '⥏',
+ '⥐' => '⥐',
+ '⥑' => '⥑',
+ '⥒' => '⥒',
+ '⥓' => '⥓',
+ '⥔' => '⥔',
+ '⥕' => '⥕',
+ '⥖' => '⥖',
+ '⥗' => '⥗',
+ '⥘' => '⥘',
+ '⥙' => '⥙',
+ '⥚' => '⥚',
+ '⥛' => '⥛',
+ '⥜' => '⥜',
+ '⥝' => '⥝',
+ '⥞' => '⥞',
+ '⥟' => '⥟',
+ '⥠' => '⥠',
+ '⥡' => '⥡',
+ '⥢' => '⥢',
+ '⥣' => '⥣',
+ '⥤' => '⥤',
+ '⥥' => '⥥',
+ '⥦' => '⥦',
+ '⥧' => '⥧',
+ '⥨' => '⥨',
+ '⥩' => '⥩',
+ '⥪' => '⥪',
+ '⥫' => '⥫',
+ '⥬' => '⥬',
+ '⥭' => '⥭',
+ '⥮' => '⥮',
+ '⥯' => '⥯',
+ '⥰' => '⥰',
+ '⥱' => '⥱',
+ '⥲' => '⥲',
+ '⥳' => '⥳',
+ '⥴' => '⥴',
+ '⥵' => '⥵',
+ '⥶' => '⥶',
+ '⥸' => '⥸',
+ '⥹' => '⥹',
+ '⥻' => '⥻',
+ '⥼' => '⥼',
+ '⥽' => '⥽',
+ '⥾' => '⥾',
+ '⥿' => '⥿',
+ '⦅' => '⦅',
+ '⦆' => '⦆',
+ '⦋' => '⦋',
+ '⦌' => '⦌',
+ '⦍' => '⦍',
+ '⦎' => '⦎',
+ '⦏' => '⦏',
+ '⦐' => '⦐',
+ '⦑' => '⦑',
+ '⦒' => '⦒',
+ '⦓' => '⦓',
+ '⦔' => '⦔',
+ '⦕' => '⦕',
+ '⦖' => '⦖',
+ '⦚' => '⦚',
+ '⦜' => '⦜',
+ '⦝' => '⦝',
+ '⦤' => '⦤',
+ '⦥' => '⦥',
+ '⦦' => '⦦',
+ '⦧' => '⦧',
+ '⦨' => '⦨',
+ '⦩' => '⦩',
+ '⦪' => '⦪',
+ '⦫' => '⦫',
+ '⦬' => '⦬',
+ '⦭' => '⦭',
+ '⦮' => '⦮',
+ '⦯' => '⦯',
+ '⦰' => '⦰',
+ '⦱' => '⦱',
+ '⦲' => '⦲',
+ '⦳' => '⦳',
+ '⦴' => '⦴',
+ '⦵' => '⦵',
+ '⦶' => '⦶',
+ '⦷' => '⦷',
+ '⦹' => '⦹',
+ '⦻' => '⦻',
+ '⦼' => '⦼',
+ '⦾' => '⦾',
+ '⦿' => '⦿',
+ '⧀' => '⧀',
+ '⧁' => '⧁',
+ '⧂' => '⧂',
+ '⧃' => '⧃',
+ '⧄' => '⧄',
+ '⧅' => '⧅',
+ '⧉' => '⧉',
+ '⧍' => '⧍',
+ '⧎' => '⧎',
+ '⧏' => '⧏',
+ '⧏̸' => '&NotLeftTriangleBar',
+ '⧐' => '⧐',
+ '⧐̸' => '&NotRightTriangleBar',
+ '⧜' => '⧜',
+ '⧝' => '⧝',
+ '⧞' => '⧞',
+ '⧣' => '⧣',
+ '⧤' => '⧤',
+ '⧥' => '⧥',
+ '⧫' => '⧫',
+ '⧴' => '⧴',
+ '⧶' => '⧶',
+ '⨀' => '⨀',
+ '⨁' => '⨁',
+ '⨂' => '⨂',
+ '⨄' => '⨄',
+ '⨆' => '⨆',
+ '⨌' => '⨌',
+ '⨍' => '⨍',
+ '⨐' => '⨐',
+ '⨑' => '⨑',
+ '⨒' => '⨒',
+ '⨓' => '⨓',
+ '⨔' => '⨔',
+ '⨕' => '⨕',
+ '⨖' => '⨖',
+ '⨗' => '⨗',
+ '⨢' => '⨢',
+ '⨣' => '⨣',
+ '⨤' => '⨤',
+ '⨥' => '⨥',
+ '⨦' => '⨦',
+ '⨧' => '⨧',
+ '⨩' => '⨩',
+ '⨪' => '⨪',
+ '⨭' => '⨭',
+ '⨮' => '⨮',
+ '⨯' => '⨯',
+ '⨰' => '⨰',
+ '⨱' => '⨱',
+ '⨳' => '⨳',
+ '⨴' => '⨴',
+ '⨵' => '⨵',
+ '⨶' => '⨶',
+ '⨷' => '⨷',
+ '⨸' => '⨸',
+ '⨹' => '⨹',
+ '⨺' => '⨺',
+ '⨻' => '⨻',
+ '⨼' => '⨼',
+ '⨿' => '⨿',
+ '⩀' => '⩀',
+ '⩂' => '⩂',
+ '⩃' => '⩃',
+ '⩄' => '⩄',
+ '⩅' => '⩅',
+ '⩆' => '⩆',
+ '⩇' => '⩇',
+ '⩈' => '⩈',
+ '⩉' => '⩉',
+ '⩊' => '⩊',
+ '⩋' => '⩋',
+ '⩌' => '⩌',
+ '⩍' => '⩍',
+ '⩐' => '⩐',
+ '⩓' => '⩓',
+ '⩔' => '⩔',
+ '⩕' => '⩕',
+ '⩖' => '⩖',
+ '⩗' => '⩗',
+ '⩘' => '⩘',
+ '⩚' => '⩚',
+ '⩛' => '⩛',
+ '⩜' => '⩜',
+ '⩝' => '⩝',
+ '⩟' => '⩟',
+ '⩦' => '⩦',
+ '⩪' => '⩪',
+ '⩭' => '⩭',
+ '⩭̸' => '&ncongdot',
+ '⩮' => '⩮',
+ '⩯' => '⩯',
+ '⩰' => '⩰',
+ '⩰̸' => '&napE',
+ '⩱' => '⩱',
+ '⩲' => '⩲',
+ '⩳' => '⩳',
+ '⩴' => '⩴',
+ '⩵' => '⩵',
+ '⩷' => '⩷',
+ '⩸' => '⩸',
+ '⩹' => '⩹',
+ '⩺' => '⩺',
+ '⩻' => '⩻',
+ '⩼' => '⩼',
+ '⩽' => '⩽',
+ '⩽̸' => '&nles',
+ '⩾' => '⩾',
+ '⩾̸' => '&nges',
+ '⩿' => '⩿',
+ '⪀' => '⪀',
+ '⪁' => '⪁',
+ '⪂' => '⪂',
+ '⪃' => '⪃',
+ '⪄' => '⪄',
+ '⪅' => '⪅',
+ '⪆' => '⪆',
+ '⪇' => '⪇',
+ '⪈' => '⪈',
+ '⪉' => '⪉',
+ '⪊' => '⪊',
+ '⪋' => '⪋',
+ '⪌' => '⪌',
+ '⪍' => '⪍',
+ '⪎' => '⪎',
+ '⪏' => '⪏',
+ '⪐' => '⪐',
+ '⪑' => '⪑',
+ '⪒' => '⪒',
+ '⪓' => '⪓',
+ '⪔' => '⪔',
+ '⪕' => '⪕',
+ '⪖' => '⪖',
+ '⪗' => '⪗',
+ '⪘' => '⪘',
+ '⪙' => '⪙',
+ '⪚' => '⪚',
+ '⪝' => '⪝',
+ '⪞' => '⪞',
+ '⪟' => '⪟',
+ '⪠' => '⪠',
+ '⪡' => '⪡',
+ '⪡̸' => '&NotNestedLessLess',
+ '⪢' => '⪢',
+ '⪢̸' => '&NotNestedGreaterGreater',
+ '⪤' => '⪤',
+ '⪥' => '⪥',
+ '⪦' => '⪦',
+ '⪧' => '⪧',
+ '⪨' => '⪨',
+ '⪩' => '⪩',
+ '⪪' => '⪪',
+ '⪫' => '⪫',
+ '⪬' => '⪬',
+ '⪬︀' => '&smtes',
+ '⪭' => '⪭',
+ '⪭︀' => '&lates',
+ '⪮' => '⪮',
+ '⪯' => '⪯',
+ '⪯̸' => '&NotPrecedesEqual',
+ '⪰' => '⪰',
+ '⪰̸' => '&NotSucceedsEqual',
+ '⪳' => '⪳',
+ '⪴' => '⪴',
+ '⪵' => '⪵',
+ '⪶' => '⪶',
+ '⪷' => '⪷',
+ '⪸' => '⪸',
+ '⪹' => '⪹',
+ '⪺' => '⪺',
+ '⪻' => '⪻',
+ '⪼' => '⪼',
+ '⪽' => '⪽',
+ '⪾' => '⪾',
+ '⪿' => '⪿',
+ '⫀' => '⫀',
+ '⫁' => '⫁',
+ '⫂' => '⫂',
+ '⫃' => '⫃',
+ '⫄' => '⫄',
+ '⫅' => '⫅',
+ '⫅̸' => '&nsubE',
+ '⫆' => '⫆',
+ '⫆̸' => '&nsupseteqq',
+ '⫇' => '⫇',
+ '⫈' => '⫈',
+ '⫋' => '⫋',
+ '⫋︀' => '&vsubnE',
+ '⫌' => '⫌',
+ '⫌︀' => '&varsupsetneqq',
+ '⫏' => '⫏',
+ '⫐' => '⫐',
+ '⫑' => '⫑',
+ '⫒' => '⫒',
+ '⫓' => '⫓',
+ '⫔' => '⫔',
+ '⫕' => '⫕',
+ '⫖' => '⫖',
+ '⫗' => '⫗',
+ '⫘' => '⫘',
+ '⫙' => '⫙',
+ '⫚' => '⫚',
+ '⫛' => '⫛',
+ '⫤' => '⫤',
+ '⫦' => '⫦',
+ '⫧' => '⫧',
+ '⫨' => '⫨',
+ '⫩' => '⫩',
+ '⫫' => '⫫',
+ '⫬' => '⫬',
+ '⫭' => '⫭',
+ '⫮' => '⫮',
+ '⫯' => '⫯',
+ '⫰' => '⫰',
+ '⫱' => '⫱',
+ '⫲' => '⫲',
+ '⫳' => '⫳',
+ '⫽︀' => '&varsupsetneqq',
+ 'ff' => 'ff',
+ 'fi' => 'fi',
+ 'fl' => 'fl',
+ 'ffi' => 'ffi',
+ 'ffl' => 'ffl',
+ '𝒜' => '𝒜',
+ '𝒞' => '𝒞',
+ '𝒟' => '𝒟',
+ '𝒢' => '𝒢',
+ '𝒥' => '𝒥',
+ '𝒦' => '𝒦',
+ '𝒩' => '𝒩',
+ '𝒪' => '𝒪',
+ '𝒫' => '𝒫',
+ '𝒬' => '𝒬',
+ '𝒮' => '𝒮',
+ '𝒯' => '𝒯',
+ '𝒰' => '𝒰',
+ '𝒱' => '𝒱',
+ '𝒲' => '𝒲',
+ '𝒳' => '𝒳',
+ '𝒴' => '𝒴',
+ '𝒵' => '𝒵',
+ '𝒶' => '𝒶',
+ '𝒷' => '𝒷',
+ '𝒸' => '𝒸',
+ '𝒹' => '𝒹',
+ '𝒻' => '𝒻',
+ '𝒽' => '𝒽',
+ '𝒾' => '𝒾',
+ '𝒿' => '𝒿',
+ '𝓀' => '𝓀',
+ '𝓁' => '𝓁',
+ '𝓂' => '𝓂',
+ '𝓃' => '𝓃',
+ '𝓅' => '𝓅',
+ '𝓆' => '𝓆',
+ '𝓇' => '𝓇',
+ '𝓈' => '𝓈',
+ '𝓉' => '𝓉',
+ '𝓊' => '𝓊',
+ '𝓋' => '𝓋',
+ '𝓌' => '𝓌',
+ '𝓍' => '𝓍',
+ '𝓎' => '𝓎',
+ '𝓏' => '𝓏',
+ '𝔄' => '𝔄',
+ '𝔅' => '𝔅',
+ '𝔇' => '𝔇',
+ '𝔈' => '𝔈',
+ '𝔉' => '𝔉',
+ '𝔊' => '𝔊',
+ '𝔍' => '𝔍',
+ '𝔎' => '𝔎',
+ '𝔏' => '𝔏',
+ '𝔐' => '𝔐',
+ '𝔑' => '𝔑',
+ '𝔒' => '𝔒',
+ '𝔓' => '𝔓',
+ '𝔔' => '𝔔',
+ '𝔖' => '𝔖',
+ '𝔗' => '𝔗',
+ '𝔘' => '𝔘',
+ '𝔙' => '𝔙',
+ '𝔚' => '𝔚',
+ '𝔛' => '𝔛',
+ '𝔜' => '𝔜',
+ '𝔞' => '𝔞',
+ '𝔟' => '𝔟',
+ '𝔠' => '𝔠',
+ '𝔡' => '𝔡',
+ '𝔢' => '𝔢',
+ '𝔣' => '𝔣',
+ '𝔤' => '𝔤',
+ '𝔥' => '𝔥',
+ '𝔦' => '𝔦',
+ '𝔧' => '𝔧',
+ '𝔨' => '𝔨',
+ '𝔩' => '𝔩',
+ '𝔪' => '𝔪',
+ '𝔫' => '𝔫',
+ '𝔬' => '𝔬',
+ '𝔭' => '𝔭',
+ '𝔮' => '𝔮',
+ '𝔯' => '𝔯',
+ '𝔰' => '𝔰',
+ '𝔱' => '𝔱',
+ '𝔲' => '𝔲',
+ '𝔳' => '𝔳',
+ '𝔴' => '𝔴',
+ '𝔵' => '𝔵',
+ '𝔶' => '𝔶',
+ '𝔷' => '𝔷',
+ '𝔸' => '𝔸',
+ '𝔹' => '𝔹',
+ '𝔻' => '𝔻',
+ '𝔼' => '𝔼',
+ '𝔽' => '𝔽',
+ '𝔾' => '𝔾',
+ '𝕀' => '𝕀',
+ '𝕁' => '𝕁',
+ '𝕂' => '𝕂',
+ '𝕃' => '𝕃',
+ '𝕄' => '𝕄',
+ '𝕆' => '𝕆',
+ '𝕊' => '𝕊',
+ '𝕋' => '𝕋',
+ '𝕌' => '𝕌',
+ '𝕍' => '𝕍',
+ '𝕎' => '𝕎',
+ '𝕏' => '𝕏',
+ '𝕐' => '𝕐',
+ '𝕒' => '𝕒',
+ '𝕓' => '𝕓',
+ '𝕔' => '𝕔',
+ '𝕕' => '𝕕',
+ '𝕖' => '𝕖',
+ '𝕗' => '𝕗',
+ '𝕘' => '𝕘',
+ '𝕙' => '𝕙',
+ '𝕚' => '𝕚',
+ '𝕛' => '𝕛',
+ '𝕜' => '𝕜',
+ '𝕝' => '𝕝',
+ '𝕞' => '𝕞',
+ '𝕟' => '𝕟',
+ '𝕠' => '𝕠',
+ '𝕡' => '𝕡',
+ '𝕢' => '𝕢',
+ '𝕣' => '𝕣',
+ '𝕤' => '𝕤',
+ '𝕥' => '𝕥',
+ '𝕦' => '𝕦',
+ '𝕧' => '𝕧',
+ '𝕨' => '𝕨',
+ '𝕩' => '𝕩',
+ '𝕪' => '𝕪',
+ '𝕫' => '𝕫',
+ );
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/OutputRules.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/OutputRules.php
new file mode 100644
index 000000000..ec467f22c
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/OutputRules.php
@@ -0,0 +1,553 @@
+'http://www.w3.org/1999/xhtml',
+ 'attrNamespace'=>'http://www.w3.org/1999/xhtml',
+
+ 'nodeName'=>'img', 'nodeName'=>array('img', 'a'),
+ 'attrName'=>'alt', 'attrName'=>array('title', 'alt'),
+ ),
+ */
+ array(
+ 'nodeNamespace' => 'http://www.w3.org/1999/xhtml',
+ 'attrName' => array('href',
+ 'hreflang',
+ 'http-equiv',
+ 'icon',
+ 'id',
+ 'keytype',
+ 'kind',
+ 'label',
+ 'lang',
+ 'language',
+ 'list',
+ 'maxlength',
+ 'media',
+ 'method',
+ 'name',
+ 'placeholder',
+ 'rel',
+ 'rows',
+ 'rowspan',
+ 'sandbox',
+ 'spellcheck',
+ 'scope',
+ 'seamless',
+ 'shape',
+ 'size',
+ 'sizes',
+ 'span',
+ 'src',
+ 'srcdoc',
+ 'srclang',
+ 'srcset',
+ 'start',
+ 'step',
+ 'style',
+ 'summary',
+ 'tabindex',
+ 'target',
+ 'title',
+ 'type',
+ 'value',
+ 'width',
+ 'border',
+ 'charset',
+ 'cite',
+ 'class',
+ 'code',
+ 'codebase',
+ 'color',
+ 'cols',
+ 'colspan',
+ 'content',
+ 'coords',
+ 'data',
+ 'datetime',
+ 'default',
+ 'dir',
+ 'dirname',
+ 'enctype',
+ 'for',
+ 'form',
+ 'formaction',
+ 'headers',
+ 'height',
+ 'accept',
+ 'accept-charset',
+ 'accesskey',
+ 'action',
+ 'align',
+ 'alt',
+ 'bgcolor',
+ ),
+ ),
+ array(
+ 'nodeNamespace' => 'http://www.w3.org/1999/xhtml',
+ 'xpath' => 'starts-with(local-name(), \'data-\')',
+ ),
+ );
+
+ const DOCTYPE = '';
+
+ public function __construct($output, $options = array())
+ {
+ if (isset($options['encode_entities'])) {
+ $this->encode = $options['encode_entities'];
+ }
+
+ $this->outputMode = static::IM_IN_HTML;
+ $this->out = $output;
+ $this->hasHTML5 = defined('ENT_HTML5');
+ }
+
+ public function addRule(array $rule)
+ {
+ $this->nonBooleanAttributes[] = $rule;
+ }
+
+ public function setTraverser(Traverser $traverser)
+ {
+ $this->traverser = $traverser;
+
+ return $this;
+ }
+
+ public function unsetTraverser()
+ {
+ $this->traverser = null;
+
+ return $this;
+ }
+
+ public function document($dom)
+ {
+ $this->doctype();
+ if ($dom->documentElement) {
+ foreach ($dom->childNodes as $node) {
+ $this->traverser->node($node);
+ }
+ $this->nl();
+ }
+ }
+
+ protected function doctype()
+ {
+ $this->wr(static::DOCTYPE);
+ $this->nl();
+ }
+
+ public function element($ele)
+ {
+ $name = $ele->tagName;
+
+ // Per spec:
+ // If the element has a declared namespace in the HTML, MathML or
+ // SVG namespaces, we use the lname instead of the tagName.
+ if ($this->traverser->isLocalElement($ele)) {
+ $name = $ele->localName;
+ }
+
+ // If we are in SVG or MathML there is special handling.
+ // Using if/elseif instead of switch because it's faster in PHP.
+ if ('svg' == $name) {
+ $this->outputMode = static::IM_IN_SVG;
+ $name = Elements::normalizeSvgElement($name);
+ } elseif ('math' == $name) {
+ $this->outputMode = static::IM_IN_MATHML;
+ }
+
+ $this->openTag($ele);
+ if (Elements::isA($name, Elements::TEXT_RAW)) {
+ foreach ($ele->childNodes as $child) {
+ if ($child instanceof \DOMCharacterData) {
+ $this->wr($child->data);
+ } elseif ($child instanceof \DOMElement) {
+ $this->element($child);
+ }
+ }
+ } else {
+ // Handle children.
+ if ($ele->hasChildNodes()) {
+ $this->traverser->children($ele->childNodes);
+ }
+
+ // Close out the SVG or MathML special handling.
+ if ('svg' == $name || 'math' == $name) {
+ $this->outputMode = static::IM_IN_HTML;
+ }
+ }
+
+ // If not unary, add a closing tag.
+ if (!Elements::isA($name, Elements::VOID_TAG)) {
+ $this->closeTag($ele);
+ }
+ }
+
+ /**
+ * Write a text node.
+ *
+ * @param \DOMText $ele The text node to write.
+ */
+ public function text($ele)
+ {
+ if (isset($ele->parentNode) && isset($ele->parentNode->tagName) && Elements::isA($ele->parentNode->localName, Elements::TEXT_RAW)) {
+ $this->wr($ele->data);
+
+ return;
+ }
+
+ // FIXME: This probably needs some flags set.
+ $this->wr($this->enc($ele->data));
+ }
+
+ public function cdata($ele)
+ {
+ // This encodes CDATA.
+ $this->wr($ele->ownerDocument->saveXML($ele));
+ }
+
+ public function comment($ele)
+ {
+ // These produce identical output.
+ // $this->wr('');
+ $this->wr($ele->ownerDocument->saveXML($ele));
+ }
+
+ public function processorInstruction($ele)
+ {
+ $this->wr('')
+ ->wr($ele->target)
+ ->wr(' ')
+ ->wr($ele->data)
+ ->wr('?>');
+ }
+
+ /**
+ * Write the namespace attributes.
+ *
+ * @param \DOMNode $ele The element being written.
+ */
+ protected function namespaceAttrs($ele)
+ {
+ if (!$this->xpath || $this->xpath->document !== $ele->ownerDocument) {
+ $this->xpath = new \DOMXPath($ele->ownerDocument);
+ }
+
+ foreach ($this->xpath->query('namespace::*[not(.=../../namespace::*)]', $ele) as $nsNode) {
+ if (!in_array($nsNode->nodeValue, $this->implicitNamespaces)) {
+ $this->wr(' ')->wr($nsNode->nodeName)->wr('="')->wr($nsNode->nodeValue)->wr('"');
+ }
+ }
+ }
+
+ /**
+ * Write the opening tag.
+ *
+ * Tags for HTML, MathML, and SVG are in the local name. Otherwise, use the
+ * qualified name (8.3).
+ *
+ * @param \DOMNode $ele The element being written.
+ */
+ protected function openTag($ele)
+ {
+ $this->wr('<')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName);
+
+ $this->attrs($ele);
+ $this->namespaceAttrs($ele);
+
+ if ($this->outputMode == static::IM_IN_HTML) {
+ $this->wr('>');
+ } // If we are not in html mode we are in SVG, MathML, or XML embedded content.
+ else {
+ if ($ele->hasChildNodes()) {
+ $this->wr('>');
+ } // If there are no children this is self closing.
+ else {
+ $this->wr(' />');
+ }
+ }
+ }
+
+ protected function attrs($ele)
+ {
+ // FIXME: Needs support for xml, xmlns, xlink, and namespaced elements.
+ if (!$ele->hasAttributes()) {
+ return $this;
+ }
+
+ // TODO: Currently, this always writes name="value", and does not do
+ // value-less attributes.
+ $map = $ele->attributes;
+ $len = $map->length;
+ for ($i = 0; $i < $len; ++$i) {
+ $node = $map->item($i);
+ $val = $this->enc($node->value, true);
+
+ // XXX: The spec says that we need to ensure that anything in
+ // the XML, XMLNS, or XLink NS's should use the canonical
+ // prefix. It seems that DOM does this for us already, but there
+ // may be exceptions.
+ $name = $node->nodeName;
+
+ // Special handling for attributes in SVG and MathML.
+ // Using if/elseif instead of switch because it's faster in PHP.
+ if ($this->outputMode == static::IM_IN_SVG) {
+ $name = Elements::normalizeSvgAttribute($name);
+ } elseif ($this->outputMode == static::IM_IN_MATHML) {
+ $name = Elements::normalizeMathMlAttribute($name);
+ }
+
+ $this->wr(' ')->wr($name);
+
+ if ((isset($val) && '' !== $val) || $this->nonBooleanAttribute($node)) {
+ $this->wr('="')->wr($val)->wr('"');
+ }
+ }
+ }
+
+ protected function nonBooleanAttribute(\DOMAttr $attr)
+ {
+ $ele = $attr->ownerElement;
+ foreach ($this->nonBooleanAttributes as $rule) {
+ if (isset($rule['nodeNamespace']) && $rule['nodeNamespace'] !== $ele->namespaceURI) {
+ continue;
+ }
+ if (isset($rule['attNamespace']) && $rule['attNamespace'] !== $attr->namespaceURI) {
+ continue;
+ }
+ if (isset($rule['nodeName']) && !is_array($rule['nodeName']) && $rule['nodeName'] !== $ele->localName) {
+ continue;
+ }
+ if (isset($rule['nodeName']) && is_array($rule['nodeName']) && !in_array($ele->localName, $rule['nodeName'], true)) {
+ continue;
+ }
+ if (isset($rule['attrName']) && !is_array($rule['attrName']) && $rule['attrName'] !== $attr->localName) {
+ continue;
+ }
+ if (isset($rule['attrName']) && is_array($rule['attrName']) && !in_array($attr->localName, $rule['attrName'], true)) {
+ continue;
+ }
+ if (isset($rule['xpath'])) {
+ $xp = $this->getXPath($attr);
+ if (isset($rule['prefixes'])) {
+ foreach ($rule['prefixes'] as $nsPrefix => $ns) {
+ $xp->registerNamespace($nsPrefix, $ns);
+ }
+ }
+ if (!$xp->evaluate($rule['xpath'], $attr)) {
+ continue;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private function getXPath(\DOMNode $node)
+ {
+ if (!$this->xpath) {
+ $this->xpath = new \DOMXPath($node->ownerDocument);
+ }
+
+ return $this->xpath;
+ }
+
+ /**
+ * Write the closing tag.
+ *
+ * Tags for HTML, MathML, and SVG are in the local name. Otherwise, use the
+ * qualified name (8.3).
+ *
+ * @param \DOMNode $ele The element being written.
+ */
+ protected function closeTag($ele)
+ {
+ if ($this->outputMode == static::IM_IN_HTML || $ele->hasChildNodes()) {
+ $this->wr('')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName)->wr('>');
+ }
+ }
+
+ /**
+ * Write to the output.
+ *
+ * @param string $text The string to put into the output
+ *
+ * @return $this
+ */
+ protected function wr($text)
+ {
+ fwrite($this->out, $text);
+
+ return $this;
+ }
+
+ /**
+ * Write a new line character.
+ *
+ * @return $this
+ */
+ protected function nl()
+ {
+ fwrite($this->out, PHP_EOL);
+
+ return $this;
+ }
+
+ /**
+ * Encode text.
+ *
+ * When encode is set to false, the default value, the text passed in is
+ * escaped per section 8.3 of the html5 spec. For details on how text is
+ * escaped see the escape() method.
+ *
+ * When encoding is set to true the text is converted to named character
+ * references where appropriate. Section 8.1.4 Character references of the
+ * html5 spec refers to using named character references. This is useful for
+ * characters that can't otherwise legally be used in the text.
+ *
+ * The named character references are listed in section 8.5.
+ *
+ * @see http://www.w3.org/TR/2013/CR-html5-20130806/syntax.html#named-character-references True encoding will turn all named character references into their entities.
+ * This includes such characters as +.# and many other common ones. By default
+ * encoding here will just escape &'<>".
+ *
+ * Note, PHP 5.4+ has better html5 encoding.
+ *
+ * @todo Use the Entities class in php 5.3 to have html5 entities.
+ *
+ * @param string $text Text to encode.
+ * @param bool $attribute True if we are encoding an attrubute, false otherwise.
+ *
+ * @return string The encoded text.
+ */
+ protected function enc($text, $attribute = false)
+ {
+ // Escape the text rather than convert to named character references.
+ if (!$this->encode) {
+ return $this->escape($text, $attribute);
+ }
+
+ // If we are in PHP 5.4+ we can use the native html5 entity functionality to
+ // convert the named character references.
+
+ if ($this->hasHTML5) {
+ return htmlentities($text, ENT_HTML5 | ENT_SUBSTITUTE | ENT_QUOTES, 'UTF-8', false);
+ } // If a version earlier than 5.4 html5 entities are not entirely handled.
+ // This manually handles them.
+ else {
+ return strtr($text, HTML5Entities::$map);
+ }
+ }
+
+ /**
+ * Escape test.
+ *
+ * According to the html5 spec section 8.3 Serializing HTML fragments, text
+ * within tags that are not style, script, xmp, iframe, noembed, and noframes
+ * need to be properly escaped.
+ *
+ * The & should be converted to &, no breaking space unicode characters
+ * converted to , when in attribute mode the " should be converted to
+ * ", and when not in attribute mode the < and > should be converted to
+ * < and >.
+ *
+ * @see http://www.w3.org/TR/2013/CR-html5-20130806/syntax.html#escapingString
+ *
+ * @param string $text Text to escape.
+ * @param bool $attribute True if we are escaping an attrubute, false otherwise.
+ */
+ protected function escape($text, $attribute = false)
+ {
+ // Not using htmlspecialchars because, while it does escaping, it doesn't
+ // match the requirements of section 8.5. For example, it doesn't handle
+ // non-breaking spaces.
+ if ($attribute) {
+ $replace = array(
+ '"' => '"',
+ '&' => '&',
+ "\xc2\xa0" => ' ',
+ );
+ } else {
+ $replace = array(
+ '<' => '<',
+ '>' => '>',
+ '&' => '&',
+ "\xc2\xa0" => ' ',
+ );
+ }
+
+ return strtr($text, $replace);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/README.md b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/README.md
new file mode 100644
index 000000000..849a47f3a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/README.md
@@ -0,0 +1,33 @@
+# The Serializer (Writer) Model
+
+The serializer roughly follows sections _8.1 Writing HTML documents_ and section
+_8.3 Serializing HTML fragments_ by converting DOMDocument, DOMDocumentFragment,
+and DOMNodeList into HTML5.
+
+ [ HTML5 ] // Interface for saving.
+ ||
+ [ Traverser ] // Walk the DOM
+ ||
+ [ Rules ] // Convert DOM elements into strings.
+ ||
+ [ HTML5 ] // HTML5 document or fragment in text.
+
+
+## HTML5 Class
+
+Provides the top level interface for saving.
+
+## The Traverser
+
+Walks the DOM finding each element and passing it off to the output rules to
+convert to HTML5.
+
+## Output Rules
+
+The output rules are defined in the RulesInterface which can have multiple
+implementations. Currently, the OutputRules is the default implementation that
+converts a DOM as is into HTML5.
+
+## HTML5 String
+
+The output of the process it HTML5 as a string or saved to a file.
\ No newline at end of file
diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/RulesInterface.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/RulesInterface.php
new file mode 100644
index 000000000..69a6ecdad
--- /dev/null
+++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/RulesInterface.php
@@ -0,0 +1,99 @@
+ 'html',
+ 'http://www.w3.org/1998/Math/MathML' => 'math',
+ 'http://www.w3.org/2000/svg' => 'svg',
+ );
+
+ protected $dom;
+
+ protected $options;
+
+ protected $encode = false;
+
+ protected $rules;
+
+ protected $out;
+
+ /**
+ * Create a traverser.
+ *
+ * @param \DOMNode|\DOMNodeList $dom The document or node to traverse.
+ * @param resource $out A stream that allows writing. The traverser will output into this
+ * stream.
+ * @param array $options An array of options for the traverser as key/value pairs. These include:
+ * - encode_entities: A bool to specify if full encding should happen for all named
+ * charachter references. Defaults to false which escapes &'<>".
+ * - output_rules: The path to the class handling the output rules.
+ */
+ public function __construct($dom, $out, RulesInterface $rules, $options = array())
+ {
+ $this->dom = $dom;
+ $this->out = $out;
+ $this->rules = $rules;
+ $this->options = $options;
+
+ $this->rules->setTraverser($this);
+ }
+
+ /**
+ * Tell the traverser to walk the DOM.
+ *
+ * @return resource $out Returns the output stream.
+ */
+ public function walk()
+ {
+ if ($this->dom instanceof \DOMDocument) {
+ $this->rules->document($this->dom);
+ } elseif ($this->dom instanceof \DOMDocumentFragment) {
+ // Document fragments are a special case. Only the children need to
+ // be serialized.
+ if ($this->dom->hasChildNodes()) {
+ $this->children($this->dom->childNodes);
+ }
+ } // If NodeList, loop
+ elseif ($this->dom instanceof \DOMNodeList) {
+ // If this is a NodeList of DOMDocuments this will not work.
+ $this->children($this->dom);
+ } // Else assume this is a DOMNode-like datastructure.
+ else {
+ $this->node($this->dom);
+ }
+
+ return $this->out;
+ }
+
+ /**
+ * Process a node in the DOM.
+ *
+ * @param mixed $node A node implementing \DOMNode.
+ */
+ public function node($node)
+ {
+ // A listing of types is at http://php.net/manual/en/dom.constants.php
+ switch ($node->nodeType) {
+ case XML_ELEMENT_NODE:
+ $this->rules->element($node);
+ break;
+ case XML_TEXT_NODE:
+ $this->rules->text($node);
+ break;
+ case XML_CDATA_SECTION_NODE:
+ $this->rules->cdata($node);
+ break;
+ case XML_PI_NODE:
+ $this->rules->processorInstruction($node);
+ break;
+ case XML_COMMENT_NODE:
+ $this->rules->comment($node);
+ break;
+ // Currently we don't support embedding DTDs.
+ default:
+ //print '';
+ break;
+ }
+ }
+
+ /**
+ * Walk through all the nodes on a node list.
+ *
+ * @param \DOMNodeList $nl A list of child elements to walk through.
+ */
+ public function children($nl)
+ {
+ foreach ($nl as $node) {
+ $this->node($node);
+ }
+ }
+
+ /**
+ * Is an element local?
+ *
+ * @param mixed $ele An element that implement \DOMNode.
+ *
+ * @return bool true if local and false otherwise.
+ */
+ public function isLocalElement($ele)
+ {
+ $uri = $ele->namespaceURI;
+ if (empty($uri)) {
+ return false;
+ }
+
+ return isset(static::$local_ns[$uri]);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/.github/workflows/phpunit.yml b/library/vendor/dompdf/vendor/phenx/php-font-lib/.github/workflows/phpunit.yml
new file mode 100644
index 000000000..82eeb8529
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/.github/workflows/phpunit.yml
@@ -0,0 +1,44 @@
+name: PHPUnit tests
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ php-version:
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version:
+ - "5.4"
+ - "5.5"
+ - "5.6"
+ - "7.0"
+ - "7.1"
+ - "7.2"
+ - "7.3"
+ - "7.4"
+ - "8.0"
+ - "8.1"
+
+ steps:
+
+ - uses: actions/checkout@v2
+
+ - name: Install PHP
+ uses: "shivammathur/setup-php@v2"
+ with:
+ php-version: "${{ matrix.php-version }}"
+ coverage: "none"
+ ini-values: "zend.assertions=1"
+
+ - name: Install Composer dependencies
+ run: composer install --no-progress --ansi
+
+ - name: Run tests ${{ matrix.php-version }}
+ run: SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 bin/simple-phpunit --color=always
diff --git a/library/vendor/dompdf/lib/php-font-lib/LICENSE b/library/vendor/dompdf/vendor/phenx/php-font-lib/LICENSE
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/LICENSE
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/LICENSE
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/README.md b/library/vendor/dompdf/vendor/phenx/php-font-lib/README.md
new file mode 100644
index 000000000..0fa24ee6b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/README.md
@@ -0,0 +1,28 @@
+[](https://github.com/dompdf/php-font-lib/actions/workflows/phpunit.yml)
+
+# PHP Font Lib
+
+This library can be used to:
+ * Read TrueType, OpenType (with TrueType glyphs), WOFF font files
+ * Extract basic info (name, style, etc)
+ * Extract advanced info (horizontal metrics, glyph names, glyph shapes, etc)
+ * Make an Adobe Font Metrics (AFM) file from a font
+
+You can find a demo GUI [here](http://pxd.me/php-font-lib/www/font_explorer.html).
+
+This project was initiated by the need to read font files in the [DOMPDF project](https://github.com/dompdf/dompdf).
+
+Usage Example
+-------------
+
+```
+$font = \FontLib\Font::load('../../fontfile.ttf');
+$font->parse(); // for getFontWeight() to work this call must be done first!
+echo $font->getFontName() .' ';
+echo $font->getFontSubfamily() .' ';
+echo $font->getFontSubfamilyID() .' ';
+echo $font->getFontFullName() .' ';
+echo $font->getFontVersion() .' ';
+echo $font->getFontWeight() .' ';
+echo $font->getFontPostscriptName() .' ';
+```
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/bower.json b/library/vendor/dompdf/vendor/phenx/php-font-lib/bower.json
new file mode 100644
index 000000000..0a4a45b2e
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/bower.json
@@ -0,0 +1,23 @@
+{
+ "name": "php-font-lib",
+ "version": "0.3.1",
+ "license": "LGPL-3.0",
+ "keywords": [
+ "font",
+ "parse",
+ "export",
+ "truetype",
+ "opentype",
+ "woff"
+ ],
+ "homepage": "https://github.com/PhenX/php-font-lib",
+ "_release": "0.3.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v0.3.1",
+ "commit": "d13682b7e27d14a6323c441426f3dde1cd86c751"
+ },
+ "_source": "https://github.com/PhenX/php-font-lib.git",
+ "_target": "*",
+ "_originalSource": "https://github.com/PhenX/php-font-lib.git"
+}
\ No newline at end of file
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/composer.json b/library/vendor/dompdf/vendor/phenx/php-font-lib/composer.json
new file mode 100644
index 000000000..29b0653a1
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/composer.json
@@ -0,0 +1,32 @@
+{
+ "name": "phenx/php-font-lib",
+ "type": "library",
+ "description": "A library to read, parse, export and make subsets of different types of font files.",
+ "homepage": "https://github.com/PhenX/php-font-lib",
+ "license": "LGPL-3.0",
+ "authors": [
+ {
+ "name": "Fabien Ménager",
+ "email": "fabien.menager@gmail.com"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "FontLib\\": "src/FontLib"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "FontLib\\Tests\\": "tests/FontLib"
+ }
+ },
+ "config": {
+ "bin-dir": "bin"
+ },
+ "require": {
+ "ext-mbstring": "*"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge" : "^3 || ^4 || ^5"
+ }
+}
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/index.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/index.php
new file mode 100644
index 000000000..7ed173a89
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/index.php
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/adobe-standard-encoding.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/adobe-standard-encoding.map
new file mode 100644
index 000000000..230d4a1ef
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/adobe-standard-encoding.map
@@ -0,0 +1,231 @@
+// Adobe Standard Encoding table for ttf2pt1
+// Thomas Henlich
+
+=20 U+0020 SPACE
+=21 U+0021 EXCLAMATION MARK
+=22 U+0022 QUOTATION MARK
+=23 U+0023 NUMBER SIGN
+=24 U+0024 DOLLAR SIGN
+=25 U+0025 PERCENT SIGN
+=26 U+0026 AMPERSAND
+=27 U+2019 RIGHT SINGLE QUOTATION MARK
+=28 U+0028 LEFT PARENTHESIS
+=29 U+0029 RIGHT PARENTHESIS
+=2A U+002A ASTERISK
+=2B U+002B PLUS SIGN
+=2C U+002C COMMA
+=2D U+002D HYPHEN-MINUS
+=2E U+002E FULL STOP
+=2F U+002F SOLIDUS
+=30 U+0030 DIGIT ZERO
+=31 U+0031 DIGIT ONE
+=32 U+0032 DIGIT TWO
+=33 U+0033 DIGIT THREE
+=34 U+0034 DIGIT FOUR
+=35 U+0035 DIGIT FIVE
+=36 U+0036 DIGIT SIX
+=37 U+0037 DIGIT SEVEN
+=38 U+0038 DIGIT EIGHT
+=39 U+0039 DIGIT NINE
+=3A U+003A COLON
+=3B U+003B SEMICOLON
+=3C U+003C LESS-THAN SIGN
+=3D U+003D EQUALS SIGN
+=3E U+003E GREATER-THAN SIGN
+=3F U+003F QUESTION MARK
+=40 U+0040 COMMERCIAL AT
+=41 U+0041 LATIN CAPITAL LETTER A
+=42 U+0042 LATIN CAPITAL LETTER B
+=43 U+0043 LATIN CAPITAL LETTER C
+=44 U+0044 LATIN CAPITAL LETTER D
+=45 U+0045 LATIN CAPITAL LETTER E
+=46 U+0046 LATIN CAPITAL LETTER F
+=47 U+0047 LATIN CAPITAL LETTER G
+=48 U+0048 LATIN CAPITAL LETTER H
+=49 U+0049 LATIN CAPITAL LETTER I
+=4A U+004A LATIN CAPITAL LETTER J
+=4B U+004B LATIN CAPITAL LETTER K
+=4C U+004C LATIN CAPITAL LETTER L
+=4D U+004D LATIN CAPITAL LETTER M
+=4E U+004E LATIN CAPITAL LETTER N
+=4F U+004F LATIN CAPITAL LETTER O
+=50 U+0050 LATIN CAPITAL LETTER P
+=51 U+0051 LATIN CAPITAL LETTER Q
+=52 U+0052 LATIN CAPITAL LETTER R
+=53 U+0053 LATIN CAPITAL LETTER S
+=54 U+0054 LATIN CAPITAL LETTER T
+=55 U+0055 LATIN CAPITAL LETTER U
+=56 U+0056 LATIN CAPITAL LETTER V
+=57 U+0057 LATIN CAPITAL LETTER W
+=58 U+0058 LATIN CAPITAL LETTER X
+=59 U+0059 LATIN CAPITAL LETTER Y
+=5A U+005A LATIN CAPITAL LETTER Z
+=5B U+005B LEFT SQUARE BRACKET
+=5C U+005C REVERSE SOLIDUS
+=5D U+005D RIGHT SQUARE BRACKET
+=5E U+005E CIRCUMFLEX ACCENT
+=5F U+005F LOW LINE
+=60 U+2018 LEFT SINGLE QUOTATION MARK
+=61 U+0061 LATIN SMALL LETTER A
+=62 U+0062 LATIN SMALL LETTER B
+=63 U+0063 LATIN SMALL LETTER C
+=64 U+0064 LATIN SMALL LETTER D
+=65 U+0065 LATIN SMALL LETTER E
+=66 U+0066 LATIN SMALL LETTER F
+=67 U+0067 LATIN SMALL LETTER G
+=68 U+0068 LATIN SMALL LETTER H
+=69 U+0069 LATIN SMALL LETTER I
+=6A U+006A LATIN SMALL LETTER J
+=6B U+006B LATIN SMALL LETTER K
+=6C U+006C LATIN SMALL LETTER L
+=6D U+006D LATIN SMALL LETTER M
+=6E U+006E LATIN SMALL LETTER N
+=6F U+006F LATIN SMALL LETTER O
+=70 U+0070 LATIN SMALL LETTER P
+=71 U+0071 LATIN SMALL LETTER Q
+=72 U+0072 LATIN SMALL LETTER R
+=73 U+0073 LATIN SMALL LETTER S
+=74 U+0074 LATIN SMALL LETTER T
+=75 U+0075 LATIN SMALL LETTER U
+=76 U+0076 LATIN SMALL LETTER V
+=77 U+0077 LATIN SMALL LETTER W
+=78 U+0078 LATIN SMALL LETTER X
+=79 U+0079 LATIN SMALL LETTER Y
+=7A U+007A LATIN SMALL LETTER Z
+=7B U+007B LEFT CURLY BRACKET
+=7C U+007C VERTICAL LINE
+=7D U+007D RIGHT CURLY BRACKET
+=7E U+007E TILDE
+=A1 U+00A1 INVERTED EXCLAMATION MARK
+=A2 U+00A2 CENT SIGN
+=A3 U+00A3 POUND SIGN
+=A4 U+2044 FRACTION SLASH
+=A5 U+00A5 YEN SIGN
+=A6 U+0192 LATIN SMALL LETTER F WITH HOOK
+=A7 U+00A7 SECTION SIGN
+=A8 U+00A4 CURRENCY SIGN
+=A9 U+0027 APOSTROPHE
+=AA U+201C LEFT DOUBLE QUOTATION MARK
+=AB U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+=AC U+2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+=AD U+203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+=AE U+FB01 LATIN SMALL LIGATURE FI
+=AF U+FB02 LATIN SMALL LIGATURE FL
+=B1 U+2013 EN DASH
+=B2 U+2020 DAGGER
+=B3 U+2021 DOUBLE DAGGER
+=B4 U+00B7 MIDDLE DOT
+=B6 U+00B6 PILCROW SIGN
+=B7 U+2022 BULLET
+=B8 U+201A SINGLE LOW-9 QUOTATION MARK
+=B9 U+201E DOUBLE LOW-9 QUOTATION MARK
+=BA U+201D RIGHT DOUBLE QUOTATION MARK
+=BB U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+=BC U+2026 HORIZONTAL ELLIPSIS
+=BD U+2030 PER MILLE SIGN
+=BF U+00BF INVERTED QUESTION MARK
+=C1 U+0060 GRAVE ACCENT
+=C2 U+00B4 ACUTE ACCENT
+=C3 U+02C6 MODIFIER LETTER CIRCUMFLEX ACCENT
+=C4 U+02DC SMALL TILDE
+=C5 U+00AF MACRON
+=C6 U+02D8 BREVE
+=C7 U+02D9 DOT ABOVE
+=C8 U+00A8 DIAERESIS
+=CA U+02DA RING ABOVE
+=CB U+00B8 CEDILLA
+=CD U+02DD DOUBLE ACUTE ACCENT
+=CE U+02DB OGONEK
+=CF U+02C7 CARON
+=D0 U+2014 EM DASH
+=E1 U+00C6 LATIN CAPITAL LETTER AE
+=E3 U+00AA FEMININE ORDINAL INDICATOR
+=E8 U+0141 LATIN CAPITAL LETTER L WITH STROKE
+=E9 U+00D8 LATIN CAPITAL LETTER O WITH STROKE
+=EA U+0152 LATIN CAPITAL LIGATURE OE
+=EB U+00BA MASCULINE ORDINAL INDICATOR
+=F1 U+00E6 LATIN SMALL LETTER AE
+=F5 U+0131 LATIN SMALL LETTER DOTLESS I
+=F8 U+0142 LATIN SMALL LETTER L WITH STROKE
+=F9 U+00F8 LATIN SMALL LETTER O WITH STROKE
+=FA U+0153 LATIN SMALL LIGATURE OE
+=FB U+00DF LATIN SMALL LETTER SHARP S
+
+// unencoded characters:
+=100 U+00E7 LATIN SMALL LETTER C WITH CEDILLA
+=101 U+00FF LATIN SMALL LETTER Y WITH DIAERESIS
+=102 U+00E3 LATIN SMALL LETTER A WITH TILDE
+=103 U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX
+=104 U+00B3 SUPERSCRIPT THREE
+=105 U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX
+=106 U+00FE LATIN SMALL LETTER THORN
+=107 U+00E8 LATIN SMALL LETTER E WITH GRAVE
+=108 U+00B2 SUPERSCRIPT TWO
+=109 U+00E9 LATIN SMALL LETTER E WITH ACUTE
+=10A U+00F5 LATIN SMALL LETTER O WITH TILDE
+=10B U+00C1 LATIN CAPITAL LETTER A WITH ACUTE
+=10C U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX
+=10D U+00FD LATIN SMALL LETTER Y WITH ACUTE
+=10E U+00FC LATIN SMALL LETTER U WITH DIAERESIS
+=10F U+00BE VULGAR FRACTION THREE QUARTERS
+=110 U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX
+=111 U+00D0 LATIN CAPITAL LETTER ETH
+=112 U+00EB LATIN SMALL LETTER E WITH DIAERESIS
+=113 U+00F9 LATIN SMALL LETTER U WITH GRAVE
+=114 U+2122 TRADE MARK SIGN
+=115 U+00F2 LATIN SMALL LETTER O WITH GRAVE
+=116 U+0161 LATIN SMALL LETTER S WITH CARON
+=117 U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS
+=118 U+00FA LATIN SMALL LETTER U WITH ACUTE
+=119 U+00E0 LATIN SMALL LETTER A WITH GRAVE
+=11A U+00F1 LATIN SMALL LETTER N WITH TILDE
+=11B U+00E5 LATIN SMALL LETTER A WITH RING ABOVE
+=11C U+017E LATIN SMALL LETTER Z WITH CARON
+=11D U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+=11E U+00D1 LATIN CAPITAL LETTER N WITH TILDE
+=11F U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX
+=120 U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+=121 U+00CD LATIN CAPITAL LETTER I WITH ACUTE
+=122 U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA
+=123 U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
+=124 U+0160 LATIN CAPITAL LETTER S WITH CARON
+=125 U+00CC LATIN CAPITAL LETTER I WITH GRAVE
+=126 U+00E4 LATIN SMALL LETTER A WITH DIAERESIS
+=127 U+00D2 LATIN CAPITAL LETTER O WITH GRAVE
+=128 U+00C8 LATIN CAPITAL LETTER E WITH GRAVE
+=129 U+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS
+=12A U+00AE REGISTERED SIGN
+=12B U+00D5 LATIN CAPITAL LETTER O WITH TILDE
+=12C U+00BC VULGAR FRACTION ONE QUARTER
+=12D U+00D9 LATIN CAPITAL LETTER U WITH GRAVE
+=12E U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+=12F U+00DE LATIN CAPITAL LETTER THORN
+=130 U+00F7 DIVISION SIGN
+=131 U+00C3 LATIN CAPITAL LETTER A WITH TILDE
+=132 U+00DA LATIN CAPITAL LETTER U WITH ACUTE
+=133 U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+=134 U+00AC NOT SIGN
+=135 U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE
+=136 U+00EF LATIN SMALL LETTER I WITH DIAERESIS
+=137 U+00ED LATIN SMALL LETTER I WITH ACUTE
+=138 U+00E1 LATIN SMALL LETTER A WITH ACUTE
+=139 U+00B1 PLUS-MINUS SIGN
+=13A U+00D7 MULTIPLICATION SIGN
+=13B U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS
+=13C U+2212 MINUS SIGN
+=13D U+00B9 SUPERSCRIPT ONE
+=13E U+00C9 LATIN CAPITAL LETTER E WITH ACUTE
+=13F U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+=140 U+00A9 COPYRIGHT SIGN
+=141 U+00C0 LATIN CAPITAL LETTER A WITH GRAVE
+=142 U+00F6 LATIN SMALL LETTER O WITH DIAERESIS
+=143 U+00F3 LATIN SMALL LETTER O WITH ACUTE
+=144 U+00B0 DEGREE SIGN
+=145 U+00EC LATIN SMALL LETTER I WITH GRAVE
+=146 U+00B5 MICRO SIGN
+=147 U+00D3 LATIN CAPITAL LETTER O WITH ACUTE
+=148 U+00F0 LATIN SMALL LETTER ETH
+=149 U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS
+=14A U+00DD LATIN CAPITAL LETTER Y WITH ACUTE
+=14B U+00A6 BROKEN BAR
+=14C U+00BD VULGAR FRACTION ONE HALF
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1250.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1250.map
new file mode 100644
index 000000000..33a555eeb
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1250.map
@@ -0,0 +1,251 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!89 U+2030 perthousand
+!8A U+0160 Scaron
+!8B U+2039 guilsinglleft
+!8C U+015A Sacute
+!8D U+0164 Tcaron
+!8E U+017D Zcaron
+!8F U+0179 Zacute
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!99 U+2122 trademark
+!9A U+0161 scaron
+!9B U+203A guilsinglright
+!9C U+015B sacute
+!9D U+0165 tcaron
+!9E U+017E zcaron
+!9F U+017A zacute
+!A0 U+00A0 space
+!A1 U+02C7 caron
+!A2 U+02D8 breve
+!A3 U+0141 Lslash
+!A4 U+00A4 currency
+!A5 U+0104 Aogonek
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+015E Scedilla
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+017B Zdotaccent
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+02DB ogonek
+!B3 U+0142 lslash
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+0105 aogonek
+!BA U+015F scedilla
+!BB U+00BB guillemotright
+!BC U+013D Lcaron
+!BD U+02DD hungarumlaut
+!BE U+013E lcaron
+!BF U+017C zdotaccent
+!C0 U+0154 Racute
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+0102 Abreve
+!C4 U+00C4 Adieresis
+!C5 U+0139 Lacute
+!C6 U+0106 Cacute
+!C7 U+00C7 Ccedilla
+!C8 U+010C Ccaron
+!C9 U+00C9 Eacute
+!CA U+0118 Eogonek
+!CB U+00CB Edieresis
+!CC U+011A Ecaron
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+010E Dcaron
+!D0 U+0110 Dcroat
+!D1 U+0143 Nacute
+!D2 U+0147 Ncaron
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+0150 Ohungarumlaut
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+0158 Rcaron
+!D9 U+016E Uring
+!DA U+00DA Uacute
+!DB U+0170 Uhungarumlaut
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+0162 Tcommaaccent
+!DF U+00DF germandbls
+!E0 U+0155 racute
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+0103 abreve
+!E4 U+00E4 adieresis
+!E5 U+013A lacute
+!E6 U+0107 cacute
+!E7 U+00E7 ccedilla
+!E8 U+010D ccaron
+!E9 U+00E9 eacute
+!EA U+0119 eogonek
+!EB U+00EB edieresis
+!EC U+011B ecaron
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+010F dcaron
+!F0 U+0111 dcroat
+!F1 U+0144 nacute
+!F2 U+0148 ncaron
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+0151 ohungarumlaut
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+0159 rcaron
+!F9 U+016F uring
+!FA U+00FA uacute
+!FB U+0171 uhungarumlaut
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+0163 tcommaaccent
+!FF U+02D9 dotaccent
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1251.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1251.map
new file mode 100644
index 000000000..b5960fe4c
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1251.map
@@ -0,0 +1,255 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0402 afii10051
+!81 U+0403 afii10052
+!82 U+201A quotesinglbase
+!83 U+0453 afii10100
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+20AC Euro
+!89 U+2030 perthousand
+!8A U+0409 afii10058
+!8B U+2039 guilsinglleft
+!8C U+040A afii10059
+!8D U+040C afii10061
+!8E U+040B afii10060
+!8F U+040F afii10145
+!90 U+0452 afii10099
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!99 U+2122 trademark
+!9A U+0459 afii10106
+!9B U+203A guilsinglright
+!9C U+045A afii10107
+!9D U+045C afii10109
+!9E U+045B afii10108
+!9F U+045F afii10193
+!A0 U+00A0 space
+!A1 U+040E afii10062
+!A2 U+045E afii10110
+!A3 U+0408 afii10057
+!A4 U+00A4 currency
+!A5 U+0490 afii10050
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+0401 afii10023
+!A9 U+00A9 copyright
+!AA U+0404 afii10053
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+0407 afii10056
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+0406 afii10055
+!B3 U+0456 afii10103
+!B4 U+0491 afii10098
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+0451 afii10071
+!B9 U+2116 afii61352
+!BA U+0454 afii10101
+!BB U+00BB guillemotright
+!BC U+0458 afii10105
+!BD U+0405 afii10054
+!BE U+0455 afii10102
+!BF U+0457 afii10104
+!C0 U+0410 afii10017
+!C1 U+0411 afii10018
+!C2 U+0412 afii10019
+!C3 U+0413 afii10020
+!C4 U+0414 afii10021
+!C5 U+0415 afii10022
+!C6 U+0416 afii10024
+!C7 U+0417 afii10025
+!C8 U+0418 afii10026
+!C9 U+0419 afii10027
+!CA U+041A afii10028
+!CB U+041B afii10029
+!CC U+041C afii10030
+!CD U+041D afii10031
+!CE U+041E afii10032
+!CF U+041F afii10033
+!D0 U+0420 afii10034
+!D1 U+0421 afii10035
+!D2 U+0422 afii10036
+!D3 U+0423 afii10037
+!D4 U+0424 afii10038
+!D5 U+0425 afii10039
+!D6 U+0426 afii10040
+!D7 U+0427 afii10041
+!D8 U+0428 afii10042
+!D9 U+0429 afii10043
+!DA U+042A afii10044
+!DB U+042B afii10045
+!DC U+042C afii10046
+!DD U+042D afii10047
+!DE U+042E afii10048
+!DF U+042F afii10049
+!E0 U+0430 afii10065
+!E1 U+0431 afii10066
+!E2 U+0432 afii10067
+!E3 U+0433 afii10068
+!E4 U+0434 afii10069
+!E5 U+0435 afii10070
+!E6 U+0436 afii10072
+!E7 U+0437 afii10073
+!E8 U+0438 afii10074
+!E9 U+0439 afii10075
+!EA U+043A afii10076
+!EB U+043B afii10077
+!EC U+043C afii10078
+!ED U+043D afii10079
+!EE U+043E afii10080
+!EF U+043F afii10081
+!F0 U+0440 afii10082
+!F1 U+0441 afii10083
+!F2 U+0442 afii10084
+!F3 U+0443 afii10085
+!F4 U+0444 afii10086
+!F5 U+0445 afii10087
+!F6 U+0446 afii10088
+!F7 U+0447 afii10089
+!F8 U+0448 afii10090
+!F9 U+0449 afii10091
+!FA U+044A afii10092
+!FB U+044B afii10093
+!FC U+044C afii10094
+!FD U+044D afii10095
+!FE U+044E afii10096
+!FF U+044F afii10097
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1252.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1252.map
new file mode 100644
index 000000000..b79015c31
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1252.map
@@ -0,0 +1,251 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+02C6 circumflex
+!89 U+2030 perthousand
+!8A U+0160 Scaron
+!8B U+2039 guilsinglleft
+!8C U+0152 OE
+!8E U+017D Zcaron
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!98 U+02DC tilde
+!99 U+2122 trademark
+!9A U+0161 scaron
+!9B U+203A guilsinglright
+!9C U+0153 oe
+!9E U+017E zcaron
+!9F U+0178 Ydieresis
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+00D0 Eth
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+00DE Thorn
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+00F0 eth
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+00FE thorn
+!FF U+00FF ydieresis
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1253.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1253.map
new file mode 100644
index 000000000..b5d843c1b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1253.map
@@ -0,0 +1,239 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!89 U+2030 perthousand
+!8B U+2039 guilsinglleft
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!99 U+2122 trademark
+!9B U+203A guilsinglright
+!A0 U+00A0 space
+!A1 U+0385 dieresistonos
+!A2 U+0386 Alphatonos
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+2015 afii00208
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+0384 tonos
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+0388 Epsilontonos
+!B9 U+0389 Etatonos
+!BA U+038A Iotatonos
+!BB U+00BB guillemotright
+!BC U+038C Omicrontonos
+!BD U+00BD onehalf
+!BE U+038E Upsilontonos
+!BF U+038F Omegatonos
+!C0 U+0390 iotadieresistonos
+!C1 U+0391 Alpha
+!C2 U+0392 Beta
+!C3 U+0393 Gamma
+!C4 U+0394 Delta
+!C5 U+0395 Epsilon
+!C6 U+0396 Zeta
+!C7 U+0397 Eta
+!C8 U+0398 Theta
+!C9 U+0399 Iota
+!CA U+039A Kappa
+!CB U+039B Lambda
+!CC U+039C Mu
+!CD U+039D Nu
+!CE U+039E Xi
+!CF U+039F Omicron
+!D0 U+03A0 Pi
+!D1 U+03A1 Rho
+!D3 U+03A3 Sigma
+!D4 U+03A4 Tau
+!D5 U+03A5 Upsilon
+!D6 U+03A6 Phi
+!D7 U+03A7 Chi
+!D8 U+03A8 Psi
+!D9 U+03A9 Omega
+!DA U+03AA Iotadieresis
+!DB U+03AB Upsilondieresis
+!DC U+03AC alphatonos
+!DD U+03AD epsilontonos
+!DE U+03AE etatonos
+!DF U+03AF iotatonos
+!E0 U+03B0 upsilondieresistonos
+!E1 U+03B1 alpha
+!E2 U+03B2 beta
+!E3 U+03B3 gamma
+!E4 U+03B4 delta
+!E5 U+03B5 epsilon
+!E6 U+03B6 zeta
+!E7 U+03B7 eta
+!E8 U+03B8 theta
+!E9 U+03B9 iota
+!EA U+03BA kappa
+!EB U+03BB lambda
+!EC U+03BC mu
+!ED U+03BD nu
+!EE U+03BE xi
+!EF U+03BF omicron
+!F0 U+03C0 pi
+!F1 U+03C1 rho
+!F2 U+03C2 sigma1
+!F3 U+03C3 sigma
+!F4 U+03C4 tau
+!F5 U+03C5 upsilon
+!F6 U+03C6 phi
+!F7 U+03C7 chi
+!F8 U+03C8 psi
+!F9 U+03C9 omega
+!FA U+03CA iotadieresis
+!FB U+03CB upsilondieresis
+!FC U+03CC omicrontonos
+!FD U+03CD upsilontonos
+!FE U+03CE omegatonos
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1254.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1254.map
new file mode 100644
index 000000000..3cc8c7894
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1254.map
@@ -0,0 +1,249 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+02C6 circumflex
+!89 U+2030 perthousand
+!8A U+0160 Scaron
+!8B U+2039 guilsinglleft
+!8C U+0152 OE
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!98 U+02DC tilde
+!99 U+2122 trademark
+!9A U+0161 scaron
+!9B U+203A guilsinglright
+!9C U+0153 oe
+!9F U+0178 Ydieresis
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+011E Gbreve
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+0130 Idotaccent
+!DE U+015E Scedilla
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+011F gbreve
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+0131 dotlessi
+!FE U+015F scedilla
+!FF U+00FF ydieresis
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1255.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1255.map
new file mode 100644
index 000000000..fa135306d
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1255.map
@@ -0,0 +1,233 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+02C6 circumflex
+!89 U+2030 perthousand
+!8B U+2039 guilsinglleft
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!98 U+02DC tilde
+!99 U+2122 trademark
+!9B U+203A guilsinglright
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+20AA afii57636
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00D7 multiply
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD sfthyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 middot
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00F7 divide
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+05B0 afii57799
+!C1 U+05B1 afii57801
+!C2 U+05B2 afii57800
+!C3 U+05B3 afii57802
+!C4 U+05B4 afii57793
+!C5 U+05B5 afii57794
+!C6 U+05B6 afii57795
+!C7 U+05B7 afii57798
+!C8 U+05B8 afii57797
+!C9 U+05B9 afii57806
+!CB U+05BB afii57796
+!CC U+05BC afii57807
+!CD U+05BD afii57839
+!CE U+05BE afii57645
+!CF U+05BF afii57841
+!D0 U+05C0 afii57842
+!D1 U+05C1 afii57804
+!D2 U+05C2 afii57803
+!D3 U+05C3 afii57658
+!D4 U+05F0 afii57716
+!D5 U+05F1 afii57717
+!D6 U+05F2 afii57718
+!D7 U+05F3 gereshhebrew
+!D8 U+05F4 gershayimhebrew
+!E0 U+05D0 afii57664
+!E1 U+05D1 afii57665
+!E2 U+05D2 afii57666
+!E3 U+05D3 afii57667
+!E4 U+05D4 afii57668
+!E5 U+05D5 afii57669
+!E6 U+05D6 afii57670
+!E7 U+05D7 afii57671
+!E8 U+05D8 afii57672
+!E9 U+05D9 afii57673
+!EA U+05DA afii57674
+!EB U+05DB afii57675
+!EC U+05DC afii57676
+!ED U+05DD afii57677
+!EE U+05DE afii57678
+!EF U+05DF afii57679
+!F0 U+05E0 afii57680
+!F1 U+05E1 afii57681
+!F2 U+05E2 afii57682
+!F3 U+05E3 afii57683
+!F4 U+05E4 afii57684
+!F5 U+05E5 afii57685
+!F6 U+05E6 afii57686
+!F7 U+05E7 afii57687
+!F8 U+05E8 afii57688
+!F9 U+05E9 afii57689
+!FA U+05EA afii57690
+!FD U+200E afii299
+!FE U+200F afii300
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1257.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1257.map
new file mode 100644
index 000000000..077fdc34a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1257.map
@@ -0,0 +1,244 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!89 U+2030 perthousand
+!8B U+2039 guilsinglleft
+!8D U+00A8 dieresis
+!8E U+02C7 caron
+!8F U+00B8 cedilla
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!99 U+2122 trademark
+!9B U+203A guilsinglright
+!9D U+00AF macron
+!9E U+02DB ogonek
+!A0 U+00A0 space
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00D8 Oslash
+!A9 U+00A9 copyright
+!AA U+0156 Rcommaaccent
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00C6 AE
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00F8 oslash
+!B9 U+00B9 onesuperior
+!BA U+0157 rcommaaccent
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00E6 ae
+!C0 U+0104 Aogonek
+!C1 U+012E Iogonek
+!C2 U+0100 Amacron
+!C3 U+0106 Cacute
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+0118 Eogonek
+!C7 U+0112 Emacron
+!C8 U+010C Ccaron
+!C9 U+00C9 Eacute
+!CA U+0179 Zacute
+!CB U+0116 Edotaccent
+!CC U+0122 Gcommaaccent
+!CD U+0136 Kcommaaccent
+!CE U+012A Imacron
+!CF U+013B Lcommaaccent
+!D0 U+0160 Scaron
+!D1 U+0143 Nacute
+!D2 U+0145 Ncommaaccent
+!D3 U+00D3 Oacute
+!D4 U+014C Omacron
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+0172 Uogonek
+!D9 U+0141 Lslash
+!DA U+015A Sacute
+!DB U+016A Umacron
+!DC U+00DC Udieresis
+!DD U+017B Zdotaccent
+!DE U+017D Zcaron
+!DF U+00DF germandbls
+!E0 U+0105 aogonek
+!E1 U+012F iogonek
+!E2 U+0101 amacron
+!E3 U+0107 cacute
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+0119 eogonek
+!E7 U+0113 emacron
+!E8 U+010D ccaron
+!E9 U+00E9 eacute
+!EA U+017A zacute
+!EB U+0117 edotaccent
+!EC U+0123 gcommaaccent
+!ED U+0137 kcommaaccent
+!EE U+012B imacron
+!EF U+013C lcommaaccent
+!F0 U+0161 scaron
+!F1 U+0144 nacute
+!F2 U+0146 ncommaaccent
+!F3 U+00F3 oacute
+!F4 U+014D omacron
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+0173 uogonek
+!F9 U+0142 lslash
+!FA U+015B sacute
+!FB U+016B umacron
+!FC U+00FC udieresis
+!FD U+017C zdotaccent
+!FE U+017E zcaron
+!FF U+02D9 dotaccent
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1258.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1258.map
new file mode 100644
index 000000000..7032c35d6
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1258.map
@@ -0,0 +1,247 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+02C6 circumflex
+!89 U+2030 perthousand
+!8B U+2039 guilsinglleft
+!8C U+0152 OE
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!98 U+02DC tilde
+!99 U+2122 trademark
+!9B U+203A guilsinglright
+!9C U+0153 oe
+!9F U+0178 Ydieresis
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+0102 Abreve
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+0300 gravecomb
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+0110 Dcroat
+!D1 U+00D1 Ntilde
+!D2 U+0309 hookabovecomb
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+01A0 Ohorn
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+01AF Uhorn
+!DE U+0303 tildecomb
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+0103 abreve
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+0301 acutecomb
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+0111 dcroat
+!F1 U+00F1 ntilde
+!F2 U+0323 dotbelowcomb
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+01A1 ohorn
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+01B0 uhorn
+!FE U+20AB dong
+!FF U+00FF ydieresis
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp874.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp874.map
new file mode 100644
index 000000000..16330d042
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp874.map
@@ -0,0 +1,225 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!85 U+2026 ellipsis
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!A0 U+00A0 space
+!A1 U+0E01 kokaithai
+!A2 U+0E02 khokhaithai
+!A3 U+0E03 khokhuatthai
+!A4 U+0E04 khokhwaithai
+!A5 U+0E05 khokhonthai
+!A6 U+0E06 khorakhangthai
+!A7 U+0E07 ngonguthai
+!A8 U+0E08 chochanthai
+!A9 U+0E09 chochingthai
+!AA U+0E0A chochangthai
+!AB U+0E0B sosothai
+!AC U+0E0C chochoethai
+!AD U+0E0D yoyingthai
+!AE U+0E0E dochadathai
+!AF U+0E0F topatakthai
+!B0 U+0E10 thothanthai
+!B1 U+0E11 thonangmonthothai
+!B2 U+0E12 thophuthaothai
+!B3 U+0E13 nonenthai
+!B4 U+0E14 dodekthai
+!B5 U+0E15 totaothai
+!B6 U+0E16 thothungthai
+!B7 U+0E17 thothahanthai
+!B8 U+0E18 thothongthai
+!B9 U+0E19 nonuthai
+!BA U+0E1A bobaimaithai
+!BB U+0E1B poplathai
+!BC U+0E1C phophungthai
+!BD U+0E1D fofathai
+!BE U+0E1E phophanthai
+!BF U+0E1F fofanthai
+!C0 U+0E20 phosamphaothai
+!C1 U+0E21 momathai
+!C2 U+0E22 yoyakthai
+!C3 U+0E23 roruathai
+!C4 U+0E24 ruthai
+!C5 U+0E25 lolingthai
+!C6 U+0E26 luthai
+!C7 U+0E27 wowaenthai
+!C8 U+0E28 sosalathai
+!C9 U+0E29 sorusithai
+!CA U+0E2A sosuathai
+!CB U+0E2B hohipthai
+!CC U+0E2C lochulathai
+!CD U+0E2D oangthai
+!CE U+0E2E honokhukthai
+!CF U+0E2F paiyannoithai
+!D0 U+0E30 saraathai
+!D1 U+0E31 maihanakatthai
+!D2 U+0E32 saraaathai
+!D3 U+0E33 saraamthai
+!D4 U+0E34 saraithai
+!D5 U+0E35 saraiithai
+!D6 U+0E36 sarauethai
+!D7 U+0E37 saraueethai
+!D8 U+0E38 sarauthai
+!D9 U+0E39 sarauuthai
+!DA U+0E3A phinthuthai
+!DF U+0E3F bahtthai
+!E0 U+0E40 saraethai
+!E1 U+0E41 saraaethai
+!E2 U+0E42 saraothai
+!E3 U+0E43 saraaimaimuanthai
+!E4 U+0E44 saraaimaimalaithai
+!E5 U+0E45 lakkhangyaothai
+!E6 U+0E46 maiyamokthai
+!E7 U+0E47 maitaikhuthai
+!E8 U+0E48 maiekthai
+!E9 U+0E49 maithothai
+!EA U+0E4A maitrithai
+!EB U+0E4B maichattawathai
+!EC U+0E4C thanthakhatthai
+!ED U+0E4D nikhahitthai
+!EE U+0E4E yamakkanthai
+!EF U+0E4F fongmanthai
+!F0 U+0E50 zerothai
+!F1 U+0E51 onethai
+!F2 U+0E52 twothai
+!F3 U+0E53 threethai
+!F4 U+0E54 fourthai
+!F5 U+0E55 fivethai
+!F6 U+0E56 sixthai
+!F7 U+0E57 seventhai
+!F8 U+0E58 eightthai
+!F9 U+0E59 ninethai
+!FA U+0E5A angkhankhuthai
+!FB U+0E5B khomutthai
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-1.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-1.map
new file mode 100644
index 000000000..74fb2fe5c
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-1.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+00D0 Eth
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+00DE Thorn
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+00F0 eth
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+00FE thorn
+!FF U+00FF ydieresis
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-11.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-11.map
new file mode 100644
index 000000000..8cf66731a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-11.map
@@ -0,0 +1,248 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0E01 kokaithai
+!A2 U+0E02 khokhaithai
+!A3 U+0E03 khokhuatthai
+!A4 U+0E04 khokhwaithai
+!A5 U+0E05 khokhonthai
+!A6 U+0E06 khorakhangthai
+!A7 U+0E07 ngonguthai
+!A8 U+0E08 chochanthai
+!A9 U+0E09 chochingthai
+!AA U+0E0A chochangthai
+!AB U+0E0B sosothai
+!AC U+0E0C chochoethai
+!AD U+0E0D yoyingthai
+!AE U+0E0E dochadathai
+!AF U+0E0F topatakthai
+!B0 U+0E10 thothanthai
+!B1 U+0E11 thonangmonthothai
+!B2 U+0E12 thophuthaothai
+!B3 U+0E13 nonenthai
+!B4 U+0E14 dodekthai
+!B5 U+0E15 totaothai
+!B6 U+0E16 thothungthai
+!B7 U+0E17 thothahanthai
+!B8 U+0E18 thothongthai
+!B9 U+0E19 nonuthai
+!BA U+0E1A bobaimaithai
+!BB U+0E1B poplathai
+!BC U+0E1C phophungthai
+!BD U+0E1D fofathai
+!BE U+0E1E phophanthai
+!BF U+0E1F fofanthai
+!C0 U+0E20 phosamphaothai
+!C1 U+0E21 momathai
+!C2 U+0E22 yoyakthai
+!C3 U+0E23 roruathai
+!C4 U+0E24 ruthai
+!C5 U+0E25 lolingthai
+!C6 U+0E26 luthai
+!C7 U+0E27 wowaenthai
+!C8 U+0E28 sosalathai
+!C9 U+0E29 sorusithai
+!CA U+0E2A sosuathai
+!CB U+0E2B hohipthai
+!CC U+0E2C lochulathai
+!CD U+0E2D oangthai
+!CE U+0E2E honokhukthai
+!CF U+0E2F paiyannoithai
+!D0 U+0E30 saraathai
+!D1 U+0E31 maihanakatthai
+!D2 U+0E32 saraaathai
+!D3 U+0E33 saraamthai
+!D4 U+0E34 saraithai
+!D5 U+0E35 saraiithai
+!D6 U+0E36 sarauethai
+!D7 U+0E37 saraueethai
+!D8 U+0E38 sarauthai
+!D9 U+0E39 sarauuthai
+!DA U+0E3A phinthuthai
+!DF U+0E3F bahtthai
+!E0 U+0E40 saraethai
+!E1 U+0E41 saraaethai
+!E2 U+0E42 saraothai
+!E3 U+0E43 saraaimaimuanthai
+!E4 U+0E44 saraaimaimalaithai
+!E5 U+0E45 lakkhangyaothai
+!E6 U+0E46 maiyamokthai
+!E7 U+0E47 maitaikhuthai
+!E8 U+0E48 maiekthai
+!E9 U+0E49 maithothai
+!EA U+0E4A maitrithai
+!EB U+0E4B maichattawathai
+!EC U+0E4C thanthakhatthai
+!ED U+0E4D nikhahitthai
+!EE U+0E4E yamakkanthai
+!EF U+0E4F fongmanthai
+!F0 U+0E50 zerothai
+!F1 U+0E51 onethai
+!F2 U+0E52 twothai
+!F3 U+0E53 threethai
+!F4 U+0E54 fourthai
+!F5 U+0E55 fivethai
+!F6 U+0E56 sixthai
+!F7 U+0E57 seventhai
+!F8 U+0E58 eightthai
+!F9 U+0E59 ninethai
+!FA U+0E5A angkhankhuthai
+!FB U+0E5B khomutthai
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-15.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-15.map
new file mode 100644
index 000000000..2689703d1
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-15.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+20AC Euro
+!A5 U+00A5 yen
+!A6 U+0160 Scaron
+!A7 U+00A7 section
+!A8 U+0161 scaron
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+017D Zcaron
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+017E zcaron
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+0152 OE
+!BD U+0153 oe
+!BE U+0178 Ydieresis
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+00D0 Eth
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+00DE Thorn
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+00F0 eth
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+00FE thorn
+!FF U+00FF ydieresis
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-16.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-16.map
new file mode 100644
index 000000000..89b802a7a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-16.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0104 Aogonek
+!A2 U+0105 aogonek
+!A3 U+0141 Lslash
+!A4 U+20AC Euro
+!A5 U+201E quotedblbase
+!A6 U+0160 Scaron
+!A7 U+00A7 section
+!A8 U+0161 scaron
+!A9 U+00A9 copyright
+!AA U+0218 Scommaaccent
+!AB U+00AB guillemotleft
+!AC U+0179 Zacute
+!AD U+00AD hyphen
+!AE U+017A zacute
+!AF U+017B Zdotaccent
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+010C Ccaron
+!B3 U+0142 lslash
+!B4 U+017D Zcaron
+!B5 U+201D quotedblright
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+017E zcaron
+!B9 U+010D ccaron
+!BA U+0219 scommaaccent
+!BB U+00BB guillemotright
+!BC U+0152 OE
+!BD U+0153 oe
+!BE U+0178 Ydieresis
+!BF U+017C zdotaccent
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+0102 Abreve
+!C4 U+00C4 Adieresis
+!C5 U+0106 Cacute
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+0110 Dcroat
+!D1 U+0143 Nacute
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+0150 Ohungarumlaut
+!D6 U+00D6 Odieresis
+!D7 U+015A Sacute
+!D8 U+0170 Uhungarumlaut
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+0118 Eogonek
+!DE U+021A Tcommaaccent
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+0103 abreve
+!E4 U+00E4 adieresis
+!E5 U+0107 cacute
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+0111 dcroat
+!F1 U+0144 nacute
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+0151 ohungarumlaut
+!F6 U+00F6 odieresis
+!F7 U+015B sacute
+!F8 U+0171 uhungarumlaut
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+0119 eogonek
+!FE U+021B tcommaaccent
+!FF U+00FF ydieresis
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-2.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-2.map
new file mode 100644
index 000000000..af9588d05
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-2.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0104 Aogonek
+!A2 U+02D8 breve
+!A3 U+0141 Lslash
+!A4 U+00A4 currency
+!A5 U+013D Lcaron
+!A6 U+015A Sacute
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+0160 Scaron
+!AA U+015E Scedilla
+!AB U+0164 Tcaron
+!AC U+0179 Zacute
+!AD U+00AD hyphen
+!AE U+017D Zcaron
+!AF U+017B Zdotaccent
+!B0 U+00B0 degree
+!B1 U+0105 aogonek
+!B2 U+02DB ogonek
+!B3 U+0142 lslash
+!B4 U+00B4 acute
+!B5 U+013E lcaron
+!B6 U+015B sacute
+!B7 U+02C7 caron
+!B8 U+00B8 cedilla
+!B9 U+0161 scaron
+!BA U+015F scedilla
+!BB U+0165 tcaron
+!BC U+017A zacute
+!BD U+02DD hungarumlaut
+!BE U+017E zcaron
+!BF U+017C zdotaccent
+!C0 U+0154 Racute
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+0102 Abreve
+!C4 U+00C4 Adieresis
+!C5 U+0139 Lacute
+!C6 U+0106 Cacute
+!C7 U+00C7 Ccedilla
+!C8 U+010C Ccaron
+!C9 U+00C9 Eacute
+!CA U+0118 Eogonek
+!CB U+00CB Edieresis
+!CC U+011A Ecaron
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+010E Dcaron
+!D0 U+0110 Dcroat
+!D1 U+0143 Nacute
+!D2 U+0147 Ncaron
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+0150 Ohungarumlaut
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+0158 Rcaron
+!D9 U+016E Uring
+!DA U+00DA Uacute
+!DB U+0170 Uhungarumlaut
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+0162 Tcommaaccent
+!DF U+00DF germandbls
+!E0 U+0155 racute
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+0103 abreve
+!E4 U+00E4 adieresis
+!E5 U+013A lacute
+!E6 U+0107 cacute
+!E7 U+00E7 ccedilla
+!E8 U+010D ccaron
+!E9 U+00E9 eacute
+!EA U+0119 eogonek
+!EB U+00EB edieresis
+!EC U+011B ecaron
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+010F dcaron
+!F0 U+0111 dcroat
+!F1 U+0144 nacute
+!F2 U+0148 ncaron
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+0151 ohungarumlaut
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+0159 rcaron
+!F9 U+016F uring
+!FA U+00FA uacute
+!FB U+0171 uhungarumlaut
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+0163 tcommaaccent
+!FF U+02D9 dotaccent
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-4.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-4.map
new file mode 100644
index 000000000..e81dd7f31
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-4.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0104 Aogonek
+!A2 U+0138 kgreenlandic
+!A3 U+0156 Rcommaaccent
+!A4 U+00A4 currency
+!A5 U+0128 Itilde
+!A6 U+013B Lcommaaccent
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+0160 Scaron
+!AA U+0112 Emacron
+!AB U+0122 Gcommaaccent
+!AC U+0166 Tbar
+!AD U+00AD hyphen
+!AE U+017D Zcaron
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+0105 aogonek
+!B2 U+02DB ogonek
+!B3 U+0157 rcommaaccent
+!B4 U+00B4 acute
+!B5 U+0129 itilde
+!B6 U+013C lcommaaccent
+!B7 U+02C7 caron
+!B8 U+00B8 cedilla
+!B9 U+0161 scaron
+!BA U+0113 emacron
+!BB U+0123 gcommaaccent
+!BC U+0167 tbar
+!BD U+014A Eng
+!BE U+017E zcaron
+!BF U+014B eng
+!C0 U+0100 Amacron
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+012E Iogonek
+!C8 U+010C Ccaron
+!C9 U+00C9 Eacute
+!CA U+0118 Eogonek
+!CB U+00CB Edieresis
+!CC U+0116 Edotaccent
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+012A Imacron
+!D0 U+0110 Dcroat
+!D1 U+0145 Ncommaaccent
+!D2 U+014C Omacron
+!D3 U+0136 Kcommaaccent
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+0172 Uogonek
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+0168 Utilde
+!DE U+016A Umacron
+!DF U+00DF germandbls
+!E0 U+0101 amacron
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+012F iogonek
+!E8 U+010D ccaron
+!E9 U+00E9 eacute
+!EA U+0119 eogonek
+!EB U+00EB edieresis
+!EC U+0117 edotaccent
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+012B imacron
+!F0 U+0111 dcroat
+!F1 U+0146 ncommaaccent
+!F2 U+014D omacron
+!F3 U+0137 kcommaaccent
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+0173 uogonek
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+0169 utilde
+!FE U+016B umacron
+!FF U+02D9 dotaccent
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-5.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-5.map
new file mode 100644
index 000000000..8030fd544
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-5.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0401 afii10023
+!A2 U+0402 afii10051
+!A3 U+0403 afii10052
+!A4 U+0404 afii10053
+!A5 U+0405 afii10054
+!A6 U+0406 afii10055
+!A7 U+0407 afii10056
+!A8 U+0408 afii10057
+!A9 U+0409 afii10058
+!AA U+040A afii10059
+!AB U+040B afii10060
+!AC U+040C afii10061
+!AD U+00AD hyphen
+!AE U+040E afii10062
+!AF U+040F afii10145
+!B0 U+0410 afii10017
+!B1 U+0411 afii10018
+!B2 U+0412 afii10019
+!B3 U+0413 afii10020
+!B4 U+0414 afii10021
+!B5 U+0415 afii10022
+!B6 U+0416 afii10024
+!B7 U+0417 afii10025
+!B8 U+0418 afii10026
+!B9 U+0419 afii10027
+!BA U+041A afii10028
+!BB U+041B afii10029
+!BC U+041C afii10030
+!BD U+041D afii10031
+!BE U+041E afii10032
+!BF U+041F afii10033
+!C0 U+0420 afii10034
+!C1 U+0421 afii10035
+!C2 U+0422 afii10036
+!C3 U+0423 afii10037
+!C4 U+0424 afii10038
+!C5 U+0425 afii10039
+!C6 U+0426 afii10040
+!C7 U+0427 afii10041
+!C8 U+0428 afii10042
+!C9 U+0429 afii10043
+!CA U+042A afii10044
+!CB U+042B afii10045
+!CC U+042C afii10046
+!CD U+042D afii10047
+!CE U+042E afii10048
+!CF U+042F afii10049
+!D0 U+0430 afii10065
+!D1 U+0431 afii10066
+!D2 U+0432 afii10067
+!D3 U+0433 afii10068
+!D4 U+0434 afii10069
+!D5 U+0435 afii10070
+!D6 U+0436 afii10072
+!D7 U+0437 afii10073
+!D8 U+0438 afii10074
+!D9 U+0439 afii10075
+!DA U+043A afii10076
+!DB U+043B afii10077
+!DC U+043C afii10078
+!DD U+043D afii10079
+!DE U+043E afii10080
+!DF U+043F afii10081
+!E0 U+0440 afii10082
+!E1 U+0441 afii10083
+!E2 U+0442 afii10084
+!E3 U+0443 afii10085
+!E4 U+0444 afii10086
+!E5 U+0445 afii10087
+!E6 U+0446 afii10088
+!E7 U+0447 afii10089
+!E8 U+0448 afii10090
+!E9 U+0449 afii10091
+!EA U+044A afii10092
+!EB U+044B afii10093
+!EC U+044C afii10094
+!ED U+044D afii10095
+!EE U+044E afii10096
+!EF U+044F afii10097
+!F0 U+2116 afii61352
+!F1 U+0451 afii10071
+!F2 U+0452 afii10099
+!F3 U+0453 afii10100
+!F4 U+0454 afii10101
+!F5 U+0455 afii10102
+!F6 U+0456 afii10103
+!F7 U+0457 afii10104
+!F8 U+0458 afii10105
+!F9 U+0459 afii10106
+!FA U+045A afii10107
+!FB U+045B afii10108
+!FC U+045C afii10109
+!FD U+00A7 section
+!FE U+045E afii10110
+!FF U+045F afii10193
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-7.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-7.map
new file mode 100644
index 000000000..be8698afc
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-7.map
@@ -0,0 +1,250 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+2018 quoteleft
+!A2 U+2019 quoteright
+!A3 U+00A3 sterling
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AF U+2015 afii00208
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+0384 tonos
+!B5 U+0385 dieresistonos
+!B6 U+0386 Alphatonos
+!B7 U+00B7 periodcentered
+!B8 U+0388 Epsilontonos
+!B9 U+0389 Etatonos
+!BA U+038A Iotatonos
+!BB U+00BB guillemotright
+!BC U+038C Omicrontonos
+!BD U+00BD onehalf
+!BE U+038E Upsilontonos
+!BF U+038F Omegatonos
+!C0 U+0390 iotadieresistonos
+!C1 U+0391 Alpha
+!C2 U+0392 Beta
+!C3 U+0393 Gamma
+!C4 U+0394 Delta
+!C5 U+0395 Epsilon
+!C6 U+0396 Zeta
+!C7 U+0397 Eta
+!C8 U+0398 Theta
+!C9 U+0399 Iota
+!CA U+039A Kappa
+!CB U+039B Lambda
+!CC U+039C Mu
+!CD U+039D Nu
+!CE U+039E Xi
+!CF U+039F Omicron
+!D0 U+03A0 Pi
+!D1 U+03A1 Rho
+!D3 U+03A3 Sigma
+!D4 U+03A4 Tau
+!D5 U+03A5 Upsilon
+!D6 U+03A6 Phi
+!D7 U+03A7 Chi
+!D8 U+03A8 Psi
+!D9 U+03A9 Omega
+!DA U+03AA Iotadieresis
+!DB U+03AB Upsilondieresis
+!DC U+03AC alphatonos
+!DD U+03AD epsilontonos
+!DE U+03AE etatonos
+!DF U+03AF iotatonos
+!E0 U+03B0 upsilondieresistonos
+!E1 U+03B1 alpha
+!E2 U+03B2 beta
+!E3 U+03B3 gamma
+!E4 U+03B4 delta
+!E5 U+03B5 epsilon
+!E6 U+03B6 zeta
+!E7 U+03B7 eta
+!E8 U+03B8 theta
+!E9 U+03B9 iota
+!EA U+03BA kappa
+!EB U+03BB lambda
+!EC U+03BC mu
+!ED U+03BD nu
+!EE U+03BE xi
+!EF U+03BF omicron
+!F0 U+03C0 pi
+!F1 U+03C1 rho
+!F2 U+03C2 sigma1
+!F3 U+03C3 sigma
+!F4 U+03C4 tau
+!F5 U+03C5 upsilon
+!F6 U+03C6 phi
+!F7 U+03C7 chi
+!F8 U+03C8 psi
+!F9 U+03C9 omega
+!FA U+03CA iotadieresis
+!FB U+03CB upsilondieresis
+!FC U+03CC omicrontonos
+!FD U+03CD upsilontonos
+!FE U+03CE omegatonos
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-9.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-9.map
new file mode 100644
index 000000000..a60bb19d4
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-9.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+011E Gbreve
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+0130 Idotaccent
+!DE U+015E Scedilla
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+011F gbreve
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+0131 dotlessi
+!FE U+015F scedilla
+!FF U+00FF ydieresis
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-r.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-r.map
new file mode 100644
index 000000000..026880d66
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-r.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+2500 SF100000
+!81 U+2502 SF110000
+!82 U+250C SF010000
+!83 U+2510 SF030000
+!84 U+2514 SF020000
+!85 U+2518 SF040000
+!86 U+251C SF080000
+!87 U+2524 SF090000
+!88 U+252C SF060000
+!89 U+2534 SF070000
+!8A U+253C SF050000
+!8B U+2580 upblock
+!8C U+2584 dnblock
+!8D U+2588 block
+!8E U+258C lfblock
+!8F U+2590 rtblock
+!90 U+2591 ltshade
+!91 U+2592 shade
+!92 U+2593 dkshade
+!93 U+2320 integraltp
+!94 U+25A0 filledbox
+!95 U+2219 periodcentered
+!96 U+221A radical
+!97 U+2248 approxequal
+!98 U+2264 lessequal
+!99 U+2265 greaterequal
+!9A U+00A0 space
+!9B U+2321 integralbt
+!9C U+00B0 degree
+!9D U+00B2 twosuperior
+!9E U+00B7 periodcentered
+!9F U+00F7 divide
+!A0 U+2550 SF430000
+!A1 U+2551 SF240000
+!A2 U+2552 SF510000
+!A3 U+0451 afii10071
+!A4 U+2553 SF520000
+!A5 U+2554 SF390000
+!A6 U+2555 SF220000
+!A7 U+2556 SF210000
+!A8 U+2557 SF250000
+!A9 U+2558 SF500000
+!AA U+2559 SF490000
+!AB U+255A SF380000
+!AC U+255B SF280000
+!AD U+255C SF270000
+!AE U+255D SF260000
+!AF U+255E SF360000
+!B0 U+255F SF370000
+!B1 U+2560 SF420000
+!B2 U+2561 SF190000
+!B3 U+0401 afii10023
+!B4 U+2562 SF200000
+!B5 U+2563 SF230000
+!B6 U+2564 SF470000
+!B7 U+2565 SF480000
+!B8 U+2566 SF410000
+!B9 U+2567 SF450000
+!BA U+2568 SF460000
+!BB U+2569 SF400000
+!BC U+256A SF540000
+!BD U+256B SF530000
+!BE U+256C SF440000
+!BF U+00A9 copyright
+!C0 U+044E afii10096
+!C1 U+0430 afii10065
+!C2 U+0431 afii10066
+!C3 U+0446 afii10088
+!C4 U+0434 afii10069
+!C5 U+0435 afii10070
+!C6 U+0444 afii10086
+!C7 U+0433 afii10068
+!C8 U+0445 afii10087
+!C9 U+0438 afii10074
+!CA U+0439 afii10075
+!CB U+043A afii10076
+!CC U+043B afii10077
+!CD U+043C afii10078
+!CE U+043D afii10079
+!CF U+043E afii10080
+!D0 U+043F afii10081
+!D1 U+044F afii10097
+!D2 U+0440 afii10082
+!D3 U+0441 afii10083
+!D4 U+0442 afii10084
+!D5 U+0443 afii10085
+!D6 U+0436 afii10072
+!D7 U+0432 afii10067
+!D8 U+044C afii10094
+!D9 U+044B afii10093
+!DA U+0437 afii10073
+!DB U+0448 afii10090
+!DC U+044D afii10095
+!DD U+0449 afii10091
+!DE U+0447 afii10089
+!DF U+044A afii10092
+!E0 U+042E afii10048
+!E1 U+0410 afii10017
+!E2 U+0411 afii10018
+!E3 U+0426 afii10040
+!E4 U+0414 afii10021
+!E5 U+0415 afii10022
+!E6 U+0424 afii10038
+!E7 U+0413 afii10020
+!E8 U+0425 afii10039
+!E9 U+0418 afii10026
+!EA U+0419 afii10027
+!EB U+041A afii10028
+!EC U+041B afii10029
+!ED U+041C afii10030
+!EE U+041D afii10031
+!EF U+041E afii10032
+!F0 U+041F afii10033
+!F1 U+042F afii10049
+!F2 U+0420 afii10034
+!F3 U+0421 afii10035
+!F4 U+0422 afii10036
+!F5 U+0423 afii10037
+!F6 U+0416 afii10024
+!F7 U+0412 afii10019
+!F8 U+042C afii10046
+!F9 U+042B afii10045
+!FA U+0417 afii10025
+!FB U+0428 afii10042
+!FC U+042D afii10047
+!FD U+0429 afii10043
+!FE U+0427 afii10041
+!FF U+042A afii10044
diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-u.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-u.map
new file mode 100644
index 000000000..97d9d031b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-u.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+2500 SF100000
+!81 U+2502 SF110000
+!82 U+250C SF010000
+!83 U+2510 SF030000
+!84 U+2514 SF020000
+!85 U+2518 SF040000
+!86 U+251C SF080000
+!87 U+2524 SF090000
+!88 U+252C SF060000
+!89 U+2534 SF070000
+!8A U+253C SF050000
+!8B U+2580 upblock
+!8C U+2584 dnblock
+!8D U+2588 block
+!8E U+258C lfblock
+!8F U+2590 rtblock
+!90 U+2591 ltshade
+!91 U+2592 shade
+!92 U+2593 dkshade
+!93 U+2320 integraltp
+!94 U+25A0 filledbox
+!95 U+2022 bullet
+!96 U+221A radical
+!97 U+2248 approxequal
+!98 U+2264 lessequal
+!99 U+2265 greaterequal
+!9A U+00A0 space
+!9B U+2321 integralbt
+!9C U+00B0 degree
+!9D U+00B2 twosuperior
+!9E U+00B7 periodcentered
+!9F U+00F7 divide
+!A0 U+2550 SF430000
+!A1 U+2551 SF240000
+!A2 U+2552 SF510000
+!A3 U+0451 afii10071
+!A4 U+0454 afii10101
+!A5 U+2554 SF390000
+!A6 U+0456 afii10103
+!A7 U+0457 afii10104
+!A8 U+2557 SF250000
+!A9 U+2558 SF500000
+!AA U+2559 SF490000
+!AB U+255A SF380000
+!AC U+255B SF280000
+!AD U+0491 afii10098
+!AE U+255D SF260000
+!AF U+255E SF360000
+!B0 U+255F SF370000
+!B1 U+2560 SF420000
+!B2 U+2561 SF190000
+!B3 U+0401 afii10023
+!B4 U+0404 afii10053
+!B5 U+2563 SF230000
+!B6 U+0406 afii10055
+!B7 U+0407 afii10056
+!B8 U+2566 SF410000
+!B9 U+2567 SF450000
+!BA U+2568 SF460000
+!BB U+2569 SF400000
+!BC U+256A SF540000
+!BD U+0490 afii10050
+!BE U+256C SF440000
+!BF U+00A9 copyright
+!C0 U+044E afii10096
+!C1 U+0430 afii10065
+!C2 U+0431 afii10066
+!C3 U+0446 afii10088
+!C4 U+0434 afii10069
+!C5 U+0435 afii10070
+!C6 U+0444 afii10086
+!C7 U+0433 afii10068
+!C8 U+0445 afii10087
+!C9 U+0438 afii10074
+!CA U+0439 afii10075
+!CB U+043A afii10076
+!CC U+043B afii10077
+!CD U+043C afii10078
+!CE U+043D afii10079
+!CF U+043E afii10080
+!D0 U+043F afii10081
+!D1 U+044F afii10097
+!D2 U+0440 afii10082
+!D3 U+0441 afii10083
+!D4 U+0442 afii10084
+!D5 U+0443 afii10085
+!D6 U+0436 afii10072
+!D7 U+0432 afii10067
+!D8 U+044C afii10094
+!D9 U+044B afii10093
+!DA U+0437 afii10073
+!DB U+0448 afii10090
+!DC U+044D afii10095
+!DD U+0449 afii10091
+!DE U+0447 afii10089
+!DF U+044A afii10092
+!E0 U+042E afii10048
+!E1 U+0410 afii10017
+!E2 U+0411 afii10018
+!E3 U+0426 afii10040
+!E4 U+0414 afii10021
+!E5 U+0415 afii10022
+!E6 U+0424 afii10038
+!E7 U+0413 afii10020
+!E8 U+0425 afii10039
+!E9 U+0418 afii10026
+!EA U+0419 afii10027
+!EB U+041A afii10028
+!EC U+041B afii10029
+!ED U+041C afii10030
+!EE U+041D afii10031
+!EF U+041E afii10032
+!F0 U+041F afii10033
+!F1 U+042F afii10049
+!F2 U+0420 afii10034
+!F3 U+0421 afii10035
+!F4 U+0422 afii10036
+!F5 U+0423 afii10037
+!F6 U+0416 afii10024
+!F7 U+0412 afii10019
+!F8 U+042C afii10046
+!F9 U+042B afii10045
+!FA U+0417 afii10025
+!FB U+0428 afii10042
+!FC U+042D afii10047
+!FD U+0429 afii10043
+!FE U+0427 afii10041
+!FF U+042A afii10044
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/AdobeFontMetrics.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/AdobeFontMetrics.php
similarity index 98%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/AdobeFontMetrics.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/AdobeFontMetrics.php
index a62daaa99..e75385f51 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/AdobeFontMetrics.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/AdobeFontMetrics.php
@@ -35,7 +35,7 @@ class AdobeFontMetrics {
$encoding = preg_replace("/[^a-z0-9-_]/", "", $encoding);
$map_file = dirname(__FILE__) . "/../maps/$encoding.map";
if (!file_exists($map_file)) {
- throw new \Exception("Unkown encoding ($encoding)");
+ throw new \Exception("Unknown encoding ($encoding)");
}
$map = new EncodingMap($map_file);
@@ -139,7 +139,7 @@ class AdobeFontMetrics {
$this->endSection("CharMetrics");
$kern = $font->getData("kern", "subtable");
- $tree = $kern["tree"];
+ $tree = is_array($kern) ? $kern["tree"] : null;
if (!$encoding && is_array($tree)) {
$this->startSection("KernData");
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Autoloader.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Autoloader.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Autoloader.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Autoloader.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/BinaryStream.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/BinaryStream.php
similarity index 97%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/BinaryStream.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/BinaryStream.php
index ab1045446..c7eb52f84 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/BinaryStream.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/BinaryStream.php
@@ -63,7 +63,7 @@ class BinaryStream {
*/
public function open($filename, $mode = self::modeRead) {
if (!in_array($mode, array(self::modeRead, self::modeWrite, self::modeReadWrite))) {
- throw new \Exception("Unkown file open mode");
+ throw new \Exception("Unknown file open mode");
}
$this->f = fopen($filename, $mode);
@@ -137,12 +137,17 @@ class BinaryStream {
fseek($this->f, $n, SEEK_CUR);
}
+ /**
+ * @param int $n The number of bytes to read
+ *
+ * @return string
+ */
public function read($n) {
if ($n < 1) {
return "";
}
- return fread($this->f, $n);
+ return (string) fread($this->f, $n);
}
public function write($data, $length = null) {
@@ -282,7 +287,7 @@ class BinaryStream {
$date = 0;
}
- return strftime("%Y-%m-%d %H:%M:%S", $date);
+ return date("Y-m-d H:i:s", $date);
}
public function writeLongDateTime($data) {
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/File.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/File.php
similarity index 93%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/File.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/File.php
index 13d592572..f51d876c5 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/File.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/File.php
@@ -61,22 +61,21 @@ class File extends \FontLib\TrueType\File {
// TODO Read font data ...
}
- /**
- * Little endian version of the read method
- *
- * @param int $n The number of bytes to read
- *
- * @return string
- */
+ /**
+ * Little endian version of the read method
+ *
+ * @param int $n The number of bytes to read
+ *
+ * @return string
+ */
public function read($n) {
if ($n < 1) {
return "";
}
- $string = fread($this->f, $n);
- $chunks = str_split($string, 2);
+ $string = (string) fread($this->f, $n);
+ $chunks = mb_str_split($string, 2, '8bit');
$chunks = array_map("strrev", $chunks);
-
return implode("", $chunks);
}
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/Header.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/Header.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/Header.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/Header.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/EncodingMap.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EncodingMap.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/EncodingMap.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EncodingMap.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Exception/FontNotFoundException.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Exception/FontNotFoundException.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Exception/FontNotFoundException.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Exception/FontNotFoundException.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Font.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Font.php
similarity index 96%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Font.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Font.php
index ecc216e81..e13a65332 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Font.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Font.php
@@ -28,7 +28,7 @@ class Font {
throw new FontNotFoundException($file);
}
- $header = file_get_contents($file, false, null, null, 4);
+ $header = file_get_contents($file, false, null, 0, 4);
$class = null;
switch ($header) {
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/Outline.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/Outline.php
similarity index 93%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/Outline.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/Outline.php
index 330db0918..639ff60aa 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/Outline.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/Outline.php
@@ -33,6 +33,9 @@ class Outline extends BinaryStream {
public $xMax;
public $yMax;
+ /**
+ * @var string|null
+ */
public $raw;
/**
@@ -75,11 +78,7 @@ class Outline extends BinaryStream {
function parse(BinaryStream $font) {
$font->seek($this->offset);
- if (!$this->size) {
- return;
- }
-
- $this->raw = $font->read($this->size);
+ $this->raw = $font->read($this->size);
}
function parseData() {
@@ -96,7 +95,7 @@ class Outline extends BinaryStream {
function encode() {
$font = $this->getFont();
- return $font->write($this->raw, strlen($this->raw));
+ return $font->write($this->raw, mb_strlen((string) $this->raw, '8bit'));
}
function getSVGContours() {
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineComponent.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineComponent.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineComponent.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineComponent.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineComposite.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineComposite.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineComposite.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineComposite.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineSimple.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineSimple.php
similarity index 99%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineSimple.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineSimple.php
index 3c023de9a..56b2fb496 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineSimple.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineSimple.php
@@ -265,7 +265,7 @@ class OutlineSimple extends Outline {
$points = $this->points;
}
- $length = count($points);
+ $length = (empty($points) ? 0 : count($points));
$firstIndex = 0;
$count = 0;
@@ -332,4 +332,4 @@ class OutlineSimple extends Outline {
function midValue($a, $b) {
return $a + ($b - $a) / 2;
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Header.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Header.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Header.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Header.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/OpenType/File.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/OpenType/File.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/OpenType/File.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/OpenType/File.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/DirectoryEntry.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/DirectoryEntry.php
similarity index 94%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/DirectoryEntry.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/DirectoryEntry.php
index 2b5846d3d..54a67af30 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/DirectoryEntry.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/DirectoryEntry.php
@@ -36,15 +36,20 @@ class DirectoryEntry extends BinaryStream {
protected $origF;
+ /**
+ * @param string $data
+ *
+ * @return int
+ */
static function computeChecksum($data) {
- $len = strlen($data);
+ $len = mb_strlen($data, '8bit');
$mod = $len % 4;
if ($mod) {
$data = str_pad($data, $len + (4 - $mod), "\0");
}
- $len = strlen($data);
+ $len = mb_strlen($data, '8bit');
$hi = 0x0000;
$lo = 0x0000;
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Table.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Table.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Table.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Table.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/cmap.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/cmap.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/cmap.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/cmap.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/glyf.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/glyf.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/glyf.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/glyf.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/head.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/head.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/head.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/head.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/hhea.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/hhea.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/hhea.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/hhea.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/hmtx.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/hmtx.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/hmtx.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/hmtx.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/kern.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/kern.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/kern.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/kern.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/loca.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/loca.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/loca.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/loca.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/maxp.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/maxp.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/maxp.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/maxp.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/name.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/name.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/name.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/name.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/nameRecord.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/nameRecord.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/nameRecord.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/nameRecord.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/os2.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/os2.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/os2.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/os2.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/post.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/post.php
similarity index 95%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/post.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/post.php
index ec5806b78..030a942e6 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/post.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/post.php
@@ -57,7 +57,9 @@ class post extends Table {
$names[$g] = File::$macCharNames[$index];
}
else {
- $names[$g] = $namesPascal[$index - 258];
+ if (array_key_exists($index - 258, $namesPascal)) {
+ $names[$g] = $namesPascal[$index - 258];
+ }
}
}
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/Collection.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/Collection.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/Collection.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/Collection.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/File.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/File.php
similarity index 99%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/File.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/File.php
index b61da0f2e..3594479aa 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/File.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/File.php
@@ -124,7 +124,7 @@ class File extends BinaryStream {
}
function utf8toUnicode($str) {
- $len = strlen($str);
+ $len = mb_strlen($str, '8bit');
$out = array();
for ($i = 0; $i < $len; $i++) {
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/Header.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/Header.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/Header.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/Header.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/File.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/File.php
similarity index 95%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/File.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/File.php
index 9e54b3fb0..4668c2391 100644
--- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/File.php
+++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/File.php
@@ -46,11 +46,11 @@ class File extends \FontLib\TrueType\File {
$data = $this->read($entry->length);
if ($entry->length < $entry->origLength) {
- $data = gzuncompress($data);
+ $data = (string) gzuncompress($data);
}
// Prepare data ...
- $length = strlen($data);
+ $length = mb_strlen($data, '8bit');
$entry->length = $entry->origLength = $length;
$entry->offset = $dataOffset;
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/Header.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/Header.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/Header.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/Header.php
diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php
similarity index 100%
rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php
rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php
diff --git a/library/vendor/dompdf/vendor/phenx/php-svg-lib/LICENSE b/library/vendor/dompdf/vendor/phenx/php-svg-lib/LICENSE
new file mode 100644
index 000000000..0a041280b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/library/vendor/dompdf/vendor/phenx/php-svg-lib/README.md b/library/vendor/dompdf/vendor/phenx/php-svg-lib/README.md
new file mode 100644
index 000000000..2b8e6f67e
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/README.md
@@ -0,0 +1,13 @@
+# SVG file parsing / rendering library
+
+[](https://github.com/phenx/php-svg-lib/actions)
+
+
+[](https://packagist.org/packages/phenx/php-svg-lib)
+[](https://packagist.org/packages/phenx/php-svg-lib)
+[](https://packagist.org/packages/phenx/php-svg-lib)
+[](https://packagist.org/packages/phenx/php-svg-lib)
+
+The main purpose of this lib is to rasterize SVG to a surface which can be an image or a PDF for example, through a `\Svg\Surface` PHP interface.
+
+This project was initialized by the need to render SVG documents inside PDF files for the [DomPdf](http://dompdf.github.io) project.
diff --git a/library/vendor/dompdf/vendor/phenx/php-svg-lib/composer.json b/library/vendor/dompdf/vendor/phenx/php-svg-lib/composer.json
new file mode 100644
index 000000000..a6ed9c541
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/composer.json
@@ -0,0 +1,31 @@
+{
+ "name": "phenx/php-svg-lib",
+ "type": "library",
+ "description": "A library to read, parse and export to PDF SVG files.",
+ "homepage": "https://github.com/PhenX/php-svg-lib",
+ "license": "LGPL-3.0",
+ "authors": [
+ {
+ "name": "Fabien Ménager",
+ "email": "fabien.menager@gmail.com"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "Svg\\": "src/Svg"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Svg\\Tests\\": "tests/Svg"
+ }
+ },
+ "require": {
+ "php": "^7.1 || ^8.0",
+ "ext-mbstring": "*",
+ "sabberworm/php-css-parser": "^8.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
+ }
+}
diff --git a/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/CssLength.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/CssLength.php
new file mode 100644
index 000000000..88eda8c6f
--- /dev/null
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/CssLength.php
@@ -0,0 +1,135 @@
+
+ */
+ protected static $inchDivisions = [
+ 'in' => 1,
+ 'cm' => 2.54,
+ 'mm' => 25.4,
+ 'q' => 101.6,
+ 'pc' => 6,
+ 'pt' => 72,
+ ];
+
+ /**
+ * The CSS length unit indicator.
+ * Will be lower-case and one of the units listed in the '$units' array or empty.
+ *
+ * @var string
+ */
+ protected $unit = '';
+
+ /**
+ * The numeric value of the given length.
+ *
+ * @var float
+ */
+ protected $value = 0;
+
+ /**
+ * The original unparsed length provided.
+ *
+ * @var string
+ */
+ protected $unparsed;
+
+ public function __construct(string $length)
+ {
+ $this->unparsed = $length;
+ $this->parseLengthComponents($length);
+ }
+
+ /**
+ * Parse out the unit and value components from the given string length.
+ */
+ protected function parseLengthComponents(string $length): void
+ {
+ $length = strtolower($length);
+
+ foreach (self::$units as $unit) {
+ $pos = strpos($length, $unit);
+ if ($pos) {
+ $this->value = floatval(substr($length, 0, $pos));
+ $this->unit = $unit;
+ return;
+ }
+ }
+
+ $this->unit = '';
+ $this->value = floatval($length);
+ }
+
+ /**
+ * Get the unit type of this css length.
+ * Units are standardised to be lower-cased.
+ *
+ * @return string
+ */
+ public function getUnit(): string
+ {
+ return $this->unit;
+ }
+
+ /**
+ * Get this CSS length in the equivalent pixel count size.
+ *
+ * @param float $referenceSize
+ * @param float $dpi
+ *
+ * @return float
+ */
+ public function toPixels(float $referenceSize = 11.0, float $dpi = 96.0): float
+ {
+ // Standard relative units
+ if (in_array($this->unit, ['em', 'rem', 'ex', 'ch'])) {
+ return $this->value * $referenceSize;
+ }
+
+ // Percentage relative units
+ if (in_array($this->unit, ['%', 'vw', 'vh', 'vmin', 'vmax'])) {
+ return $this->value * ($referenceSize / 100);
+ }
+
+ // Inch relative units
+ if (in_array($this->unit, array_keys(static::$inchDivisions))) {
+ $inchValue = $this->value * $dpi;
+ $division = static::$inchDivisions[$this->unit];
+ return $inchValue / $division;
+ }
+
+ return $this->value;
+ }
+}
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/DefaultStyle.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/DefaultStyle.php
similarity index 82%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/DefaultStyle.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/DefaultStyle.php
index c0535c726..4e73d2948 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/DefaultStyle.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/DefaultStyle.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -10,11 +10,11 @@ namespace Svg;
class DefaultStyle extends Style
{
- public $color = '';
+ public $color = [0, 0, 0, 1];
public $opacity = 1.0;
public $display = 'inline';
- public $fill = 'black';
+ public $fill = [0, 0, 0, 1];
public $fillOpacity = 1.0;
public $fillRule = 'nonzero';
@@ -26,4 +26,4 @@ class DefaultStyle extends Style
public $strokeWidth = 1.0;
public $strokeDasharray = 0;
public $strokeDashoffset = 0;
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Document.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Document.php
similarity index 97%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Document.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Document.php
index 4ecdc761a..4de226e70 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Document.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Document.php
@@ -41,9 +41,6 @@ class Document extends AbstractTag
protected $pathBBox;
protected $viewBox;
- /** @var resource */
- protected $parser;
-
/** @var SurfaceInterface */
protected $surface;
@@ -74,7 +71,7 @@ class Document extends AbstractTag
array($this, "_charData")
);
- return $this->parser = $parser;
+ return $parser;
}
public function __construct() {
@@ -104,6 +101,11 @@ class Document extends AbstractTag
return $this->height;
}
+ public function getDiagonal()
+ {
+ return sqrt(($this->width)**2 + ($this->height)**2) / sqrt(2);
+ }
+
public function getDimensions() {
$rootAttributes = null;
@@ -138,12 +140,12 @@ class Document extends AbstractTag
public function handleSizeAttributes($attributes){
if ($this->width === null) {
if (isset($attributes["width"])) {
- $width = Style::convertSize($attributes["width"], 400);
+ $width = $this->convertSize($attributes["width"], 400);
$this->width = $width;
}
if (isset($attributes["height"])) {
- $height = Style::convertSize($attributes["height"], 300);
+ $height = $this->convertSize($attributes["height"], 300);
$this->height = $height;
}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Gradient/Stop.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Gradient/Stop.php
similarity index 81%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Gradient/Stop.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Gradient/Stop.php
index 14a36bdec..a37fb970b 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Gradient/Stop.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Gradient/Stop.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -13,4 +13,4 @@ class Stop
public $offset;
public $color;
public $opacity = 1.0;
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Style.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Style.php
similarity index 88%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Style.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Style.php
index 9e7f6dba0..14b11e902 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Style.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Style.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien M�nager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -18,6 +18,8 @@ class Style
const TYPE_ANGLE = 4;
const TYPE_NUMBER = 5;
+ private $_parentStyle;
+
public $color;
public $opacity;
public $display;
@@ -88,7 +90,7 @@ class Style
$group = $tag->getParentGroup();
if ($group) {
$parent_style = $group->getStyle();
-
+ $this->_parentStyle = $parent_style;
foreach ($parent_style as $_key => $_value) {
if ($_value !== null) {
$this->$_key = $_value;
@@ -145,13 +147,24 @@ class Style
protected function fillStyles($styles)
{
- foreach ($this->getStyleMap() as $from => $spec) {
+ $style_map = $this->getStyleMap();
+ foreach ($style_map as $from => $spec) {
if (isset($styles[$from])) {
list($to, $type) = $spec;
$value = null;
switch ($type) {
case self::TYPE_COLOR:
$value = self::parseColor($styles[$from]);
+ if ($value === "currentcolor") {
+ if ($type === "color") {
+ $value = $this->_parentStyle->color;
+ } else {
+ $value = $this->color;
+ }
+ }
+ if ($value !== null && $value[3] !== 1 && array_key_exists("{$from}-opacity", $style_map) === true) {
+ $styles["{$from}-opacity"] = $value[3];
+ }
break;
case self::TYPE_NUMBER:
@@ -177,8 +190,7 @@ class Style
if (count($parts) == 2) {
$color = $parts[1];
- }
- else {
+ } else {
$color = $parts[0];
}
@@ -186,6 +198,10 @@ class Style
return "none";
}
+ if ($color === "currentcolor") {
+ return "currentcolor";
+ }
+
// SVG color name
if (isset(self::$colorNames[$color])) {
return self::parseHexColor(self::$colorNames[$color]);
@@ -198,18 +214,18 @@ class Style
// RGB color
if (strpos($color, "rgb") !== false) {
- return self::getTriplet($color);
+ return self::getQuad($color);
}
// RGB color
if (strpos($color, "hsl") !== false) {
- $triplet = self::getTriplet($color, true);
+ $quad = self::getQuad($color, true);
- if ($triplet == null) {
+ if ($quad == null) {
return null;
}
- list($h, $s, $l) = $triplet;
+ list($h, $s, $l, $a) = $quad;
$r = $l;
$g = $l;
@@ -258,11 +274,13 @@ class Style
break;
}
}
+ $a = $a * 255;
return array(
$r * 255.0,
$g * 255.0,
$b * 255.0,
+ $a
);
}
@@ -282,7 +300,7 @@ class Style
return null;
}
- static function getTriplet($color, $percent = false) {
+ static function getQuad($color, $percent = false) {
$i = strpos($color, "(");
$j = strpos($color, ")");
@@ -291,46 +309,60 @@ class Style
return null;
}
- $triplet = preg_split("/\\s*,\\s*/", trim(substr($color, $i + 1, $j - $i - 1)));
+ $quad = preg_split("/\\s*[,\\/]\\s*/", trim(substr($color, $i + 1, $j - $i - 1)));
+ if (!isset($quad[3])) {
+ $quad[3] = 1;
+ }
- if (count($triplet) != 3) {
+ if (count($quad) != 3 && count($quad) != 4) {
return null;
}
- foreach (array_keys($triplet) as $c) {
- $triplet[$c] = trim($triplet[$c]);
+ foreach (array_keys($quad) as $c) {
+ $quad[$c] = trim($quad[$c]);
if ($percent) {
- if ($triplet[$c][strlen($triplet[$c]) - 1] === "%") {
- $triplet[$c] = $triplet[$c] / 100;
+ if ($quad[$c][strlen($quad[$c]) - 1] === "%") {
+ $quad[$c] = floatval($quad[$c]) / 100;
+ } else {
+ $quad[$c] = $quad[$c] / 255;
}
- else {
- $triplet[$c] = $triplet[$c] / 255;
- }
- }
- else {
- if ($triplet[$c][strlen($triplet[$c]) - 1] === "%") {
- $triplet[$c] = round($triplet[$c] * 2.55);
+ } else {
+ if ($quad[$c][strlen($quad[$c]) - 1] === "%") {
+ $quad[$c] = round(floatval($quad[$c]) * 2.55);
}
}
}
- return $triplet;
+ return $quad;
}
static function parseHexColor($hex)
{
- $c = array(0, 0, 0);
+ $c = array(0, 0, 0, 1);
// #FFFFFF
if (isset($hex[6])) {
$c[0] = hexdec(substr($hex, 1, 2));
$c[1] = hexdec(substr($hex, 3, 2));
$c[2] = hexdec(substr($hex, 5, 2));
+
+ if (isset($hex[7])) {
+ $alpha = substr($hex, 7, 2);
+ if (ctype_xdigit($alpha)) {
+ $c[3] = round(hexdec($alpha)/255, 2);
+ }
+ }
} else {
$c[0] = hexdec($hex[1] . $hex[1]);
$c[1] = hexdec($hex[2] . $hex[2]);
$c[2] = hexdec($hex[3] . $hex[3]);
+
+ if (isset($hex[4])) {
+ if (ctype_xdigit($hex[4])) {
+ $c[3] = round(hexdec($hex[4] . $hex[4])/255, 2);
+ }
+ }
}
return $c;
@@ -356,47 +388,6 @@ class Style
return $styles;
}
- /**
- * Convert a size to a float
- *
- * @param string $size SVG size
- * @param float $dpi DPI
- * @param float $referenceSize Reference size
- *
- * @return float|null
- */
- static function convertSize($size, $referenceSize = 11.0, $dpi = 96.0) {
- $size = trim(strtolower($size));
-
- if (is_numeric($size)) {
- return $size;
- }
-
- if ($pos = strpos($size, "px")) {
- return floatval(substr($size, 0, $pos));
- }
-
- if ($pos = strpos($size, "pt")) {
- return floatval(substr($size, 0, $pos));
- }
-
- if ($pos = strpos($size, "cm")) {
- return floatval(substr($size, 0, $pos)) * $dpi;
- }
-
- if ($pos = strpos($size, "%")) {
- return $referenceSize * substr($size, 0, $pos) / 100;
- }
-
- if ($pos = strpos($size, "em")) {
- return $referenceSize * substr($size, 0, $pos);
- }
-
- // TODO cm, mm, pc, in, etc
-
- return null;
- }
-
static $colorNames = array(
'antiquewhite' => '#FAEBD7',
'aqua' => '#00FFFF',
@@ -547,4 +538,4 @@ class Style
'whitesmoke' => '#f5f5f5',
'yellowgreen' => '#9acd32',
);
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/Cpdf.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/CPdf.php
similarity index 77%
rename from library/vendor/dompdf/lib/Cpdf.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/CPdf.php
index 7748d4f15..caa28a890 100644
--- a/library/vendor/dompdf/lib/Cpdf.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/CPdf.php
@@ -15,11 +15,34 @@
* @license Public Domain http://creativecommons.org/licenses/publicdomain/
* @package Cpdf
*/
-use FontLib\Font;
-use FontLib\BinaryStream;
-class Cpdf
+namespace Svg\Surface;
+
+class CPdf
{
+ const PDF_VERSION = '1.7';
+
+ const ACROFORM_SIG_SIGNATURESEXISTS = 0x0001;
+ const ACROFORM_SIG_APPENDONLY = 0x0002;
+
+ const ACROFORM_FIELD_BUTTON = 'Btn';
+ const ACROFORM_FIELD_TEXT = 'Tx';
+ const ACROFORM_FIELD_CHOICE = 'Ch';
+ const ACROFORM_FIELD_SIG = 'Sig';
+
+ const ACROFORM_FIELD_READONLY = 0x0001;
+ const ACROFORM_FIELD_REQUIRED = 0x0002;
+
+ const ACROFORM_FIELD_TEXT_MULTILINE = 0x1000;
+ const ACROFORM_FIELD_TEXT_PASSWORD = 0x2000;
+ const ACROFORM_FIELD_TEXT_RICHTEXT = 0x10000;
+
+ const ACROFORM_FIELD_CHOICE_COMBO = 0x20000;
+ const ACROFORM_FIELD_CHOICE_EDIT = 0x40000;
+ const ACROFORM_FIELD_CHOICE_SORT = 0x80000;
+ const ACROFORM_FIELD_CHOICE_MULTISELECT = 0x200000;
+
+ const XOBJECT_SUBTYPE_FORM = 'Form';
/**
* @var integer The current number of pdf objects in the document
@@ -29,18 +52,40 @@ class Cpdf
/**
* @var array This array contains all of the pdf objects, ready for final assembly
*/
- public $objects = array();
+ public $objects = [];
/**
* @var integer The objectId (number within the objects array) of the document catalog
*/
public $catalogId;
+ /**
+ * @var integer The objectId (number within the objects array) of indirect references (Javascript EmbeddedFiles)
+ */
+ protected $indirectReferenceId = 0;
+
+ /**
+ * @var integer The objectId (number within the objects array)
+ */
+ protected $embeddedFilesId = 0;
+
+ /**
+ * AcroForm objectId
+ *
+ * @var integer
+ */
+ public $acroFormId;
+
+ /**
+ * @var int
+ */
+ public $signatureMaxLen = 5000;
+
/**
* @var array Array carrying information about the fonts that the system currently knows about
* Used to ensure that a font is not loaded twice, among other things
*/
- public $fonts = array();
+ public $fonts = [];
/**
* @var string The default font metrics file to use if no other font has been loaded.
@@ -91,7 +136,7 @@ class Cpdf
/**
* @var array Number of graphic state resources used
*/
- private $gstates = array();
+ private $gstates = [];
/**
* @var array Current color for fill operations, defaults to inactive value,
@@ -117,18 +162,18 @@ class Cpdf
/**
* @var array Current line transparency (partial graphics state)
*/
- public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
+ public $currentLineTransparency = ["mode" => "Normal", "opacity" => 1.0];
/**
* array Current fill transparency (partial graphics state)
*/
- public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
+ public $currentFillTransparency = ["mode" => "Normal", "opacity" => 1.0];
/**
* @var array An array which is used to save the state of the document, mainly the colors and styles
* it is used to temporarily change to another state, then change back to what it was before
*/
- public $stateStack = array();
+ public $stateStack = [];
/**
* @var integer Number of elements within the state stack
@@ -143,7 +188,7 @@ class Cpdf
/**
* @var array Object Id storage stack
*/
- public $stack = array();
+ public $stack = [];
/**
* @var integer Number of elements within the object Id storage stack
@@ -154,12 +199,12 @@ class Cpdf
* an array which contains information about the objects which are not firmly attached to pages
* these have been added with the addObject function
*/
- public $looseObjects = array();
+ public $looseObjects = [];
/**
* array contains information about how the loose objects are to be added to the document
*/
- public $addLooseObjects = array();
+ public $addLooseObjects = [];
/**
* @var integer The objectId of the information object for the document
@@ -176,7 +221,7 @@ class Cpdf
* @var array An array containing options about the document
* it defaults to turning on the compression of the objects
*/
- public $options = array('compression' => true);
+ public $options = ['compression' => true];
/**
* @var integer The objectId of the first page of the document
@@ -193,7 +238,7 @@ class Cpdf
* this used so that the code knows which font is the bold version of another font, etc.
* the value of this array is initialised in the constructor function.
*/
- public $fontFamilies = array();
+ public $fontFamilies = [];
/**
* @var string Folder for php serialized formats of font metrics files.
@@ -255,7 +300,7 @@ class Cpdf
/**
* @var array Array which forms a stack to keep track of nested callback functions
*/
- public $callback = array();
+ public $callback = [];
/**
* @var integer The number of callback functions in the callback array
@@ -266,7 +311,7 @@ class Cpdf
* @var array Store label->id pairs for named destinations, these will be used to replace internal links
* done this way so that destinations can be defined after the location that links to them
*/
- public $destinations = array();
+ public $destinations = [];
/**
* @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
@@ -279,7 +324,17 @@ class Cpdf
* @var array Table of Image origin filenames and image labels which were already added with o_image().
* Allows to merge identical images
*/
- public $imagelist = array();
+ public $imagelist = [];
+
+ /**
+ * @var array Table of already added alpha and plain image files for transparent PNG images.
+ */
+ protected $imageAlphaList = [];
+
+ /**
+ * @var array List of temporary image files to be deleted after processing.
+ */
+ protected $imageCache = [];
/**
* @var boolean Whether the text passed in should be treated as Unicode or just local character set.
@@ -299,22 +354,27 @@ class Cpdf
/**
* @var array Current page size
*/
- protected $currentPageSize = array("width" => 0, "height" => 0);
+ protected $currentPageSize = ["width" => 0, "height" => 0];
/**
* @var array All the chars that will be required in the font subsets
*/
- protected $stringSubsets = array();
+ protected $stringSubsets = [];
/**
* @var string The target internal encoding
*/
- static protected $targetEncoding = 'Windows-1252';
+ protected static $targetEncoding = 'Windows-1252';
+
+ /**
+ * @var array
+ */
+ protected $byteRange = array();
/**
* @var array The list of the core fonts
*/
- static protected $coreFonts = array(
+ protected static $coreFonts = [
'courier',
'courier-bold',
'courier-oblique',
@@ -329,7 +389,7 @@ class Cpdf
'times-bolditalic',
'symbol',
'zapfdingbats'
- );
+ ];
/**
* Class constructor
@@ -340,7 +400,7 @@ class Cpdf
* @param string $fontcache The font cache folder
* @param string $tmp The temporary folder
*/
- function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '')
+ function __construct($pageSize = [0, 0, 612, 792], $isUnicode = false, $fontcache = '', $tmp = '')
{
$this->isUnicode = $isUnicode;
$this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\");
@@ -357,6 +417,15 @@ class Cpdf
$this->setFontFamily('init');
}
+ public function __destruct()
+ {
+ foreach ($this->imageCache as $file) {
+ if (file_exists($file)) {
+ unlink($file);
+ }
+ }
+ }
+
/**
* Document object methods (internal use only)
*
@@ -384,7 +453,7 @@ class Cpdf
{
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'destination', 'info' => array());
+ $this->objects[$id] = ['t' => 'destination', 'info' => []];
$tmp = '';
switch ($options['type']) {
case 'XYZ':
@@ -429,7 +498,7 @@ class Cpdf
{
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
+ $this->objects[$id] = ['t' => 'viewerPreferences', 'info' => []];
break;
case 'add':
@@ -463,28 +532,28 @@ class Cpdf
// Named with limited valid values
case 'NonFullScreenPageMode':
- if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) {
+ if (!in_array($v, ['UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'])) {
break;
}
$o['info'][$k] = $v;
break;
case 'Direction':
- if (!in_array($v, array('L2R', 'R2L'))) {
+ if (!in_array($v, ['L2R', 'R2L'])) {
break;
}
$o['info'][$k] = $v;
break;
case 'PrintScaling':
- if (!in_array($v, array('None', 'AppDefault'))) {
+ if (!in_array($v, ['None', 'AppDefault'])) {
break;
}
$o['info'][$k] = $v;
break;
case 'Duplex':
- if (!in_array($v, array('None', 'AppDefault'))) {
+ if (!in_array($v, ['None', 'Simplex', 'DuplexFlipShortEdge', 'DuplexFlipLongEdge'])) {
break;
}
$o['info'][$k] = $v;
@@ -518,7 +587,7 @@ class Cpdf
}
$res .= "\n/$k $v";
}
- $res .= "\n>>\n";
+ $res .= "\n>>\nendobj";
return $res;
}
@@ -542,14 +611,15 @@ class Cpdf
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'catalog', 'info' => array());
+ $this->objects[$id] = ['t' => 'catalog', 'info' => []];
$this->catalogId = $id;
break;
+ case 'acroform':
case 'outlines':
case 'pages':
case 'openHere':
- case 'javascript':
+ case 'names':
$o['info'][$action] = $options;
break;
@@ -586,8 +656,12 @@ class Cpdf
$res .= "\n/OpenAction $v 0 R";
break;
- case 'javascript':
- $res .= "\n/Names <>";
+ case 'names':
+ $res .= "\n/Names $v 0 R";
+ break;
+
+ case 'acroform':
+ $res .= "\n/AcroForm $v 0 R";
break;
}
}
@@ -616,7 +690,7 @@ class Cpdf
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'pages', 'info' => array());
+ $this->objects[$id] = ['t' => 'pages', 'info' => []];
$this->o_catalog($this->catalogId, 'pages', $id);
break;
@@ -666,19 +740,19 @@ class Cpdf
case 'mediaBox':
$o['info']['mediaBox'] = $options;
// which should be an array of 4 numbers
- $this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
+ $this->currentPageSize = ['width' => $options[2], 'height' => $options[3]];
break;
case 'font':
- $o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
+ $o['info']['fonts'][] = ['objNum' => $options['objNum'], 'fontNum' => $options['fontNum']];
break;
case 'extGState':
- $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
+ $o['info']['extGStates'][] = ['objNum' => $options['objNum'], 'stateNum' => $options['stateNum']];
break;
case 'xObject':
- $o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
+ $o['info']['xObjects'][] = ['objNum' => $options['objNum'], 'label' => $options['label']];
break;
case 'out':
@@ -764,7 +838,7 @@ class Cpdf
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
+ $this->objects[$id] = ['t' => 'outlines', 'info' => ['outlines' => []]];
$this->o_catalog($this->catalogId, 'outlines', $id);
break;
@@ -797,6 +871,7 @@ class Cpdf
* @param $action
* @param string|array $options
* @return string|null
+ * @throws FontNotFoundException
*/
protected function o_font($id, $action, $options = '')
{
@@ -806,14 +881,15 @@ class Cpdf
switch ($action) {
case 'new':
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'font',
- 'info' => array(
+ 'info' => [
'name' => $options['name'],
'fontFileName' => $options['fontFileName'],
- 'SubType' => 'Type1'
- )
- );
+ 'SubType' => 'Type1',
+ 'isSubsetting' => $options['isSubsetting']
+ ]
+ ];
$fontNum = $this->numFonts;
$this->objects[$id]['info']['fontNum'] = $fontNum;
@@ -864,29 +940,33 @@ class Cpdf
}
// also tell the pages node about the new font
- $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
+ $this->o_pages($this->currentNode, 'font', ['fontNum' => $fontNum, 'objNum' => $id]);
break;
case 'add':
- foreach ($options as $k => $v) {
- switch ($k) {
- case 'BaseFont':
- $o['info']['name'] = $v;
- break;
- case 'FirstChar':
- case 'LastChar':
- case 'Widths':
- case 'FontDescriptor':
- case 'SubType':
- $this->addMessage('o_font ' . $k . " : " . $v);
- $o['info'][$k] = $v;
- break;
- }
- }
+ $font_options = $this->processFont($id, $o['info']);
- // pass values down to descendent font
- if (isset($o['info']['cidFont'])) {
- $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
+ if ($font_options !== false) {
+ foreach ($font_options as $k => $v) {
+ switch ($k) {
+ case 'BaseFont':
+ $o['info']['name'] = $v;
+ break;
+ case 'FirstChar':
+ case 'LastChar':
+ case 'Widths':
+ case 'FontDescriptor':
+ case 'SubType':
+ $this->addMessage('o_font ' . $k . " : " . $v);
+ $o['info'][$k] = $v;
+ break;
+ }
+ }
+
+ // pass values down to descendent font
+ if (isset($o['info']['cidFont'])) {
+ $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $font_options);
+ }
}
break;
@@ -951,6 +1031,237 @@ class Cpdf
return null;
}
+ protected function getFontSubsettingTag(array $font): string
+ {
+ // convert font num to hexavigesimal numeral system letters A - Z only
+ $base_26 = strtoupper(base_convert($font['fontNum'], 10, 26));
+ for ($i = 0; $i < strlen($base_26); $i++) {
+ $char = $base_26[$i];
+ if ($char <= "9") {
+ $base_26[$i] = chr(65 + intval($char));
+ } else {
+ $base_26[$i] = chr(ord($char) + 10);
+ }
+ }
+
+ return 'SUB' . str_pad($base_26, 3 , 'A', STR_PAD_LEFT);
+ }
+
+ /**
+ * @param int $fontObjId
+ * @param array $object_info
+ * @return array|false
+ * @throws FontNotFoundException
+ */
+ private function processFont(int $fontObjId, array $object_info)
+ {
+ $fontFileName = $object_info['fontFileName'];
+ if (!isset($this->fonts[$fontFileName])) {
+ return false;
+ }
+
+ $font = &$this->fonts[$fontFileName];
+
+ $fileSuffix = $font['fileSuffix'];
+ $fileSuffixLower = strtolower($font['fileSuffix']);
+ $fbfile = "$fontFileName.$fileSuffix";
+ $isTtfFont = $fileSuffixLower === 'ttf';
+ $isPfbFont = $fileSuffixLower === 'pfb';
+
+ $this->addMessage('selectFont: checking for - ' . $fbfile);
+
+ if (!$fileSuffix) {
+ $this->addMessage(
+ 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
+ );
+
+ return false;
+ } else {
+ $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
+ // $fontObj = $this->numObj;
+ $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
+
+ // find the array of font widths, and put that into an object.
+ $firstChar = -1;
+ $lastChar = 0;
+ $widths = [];
+ $cid_widths = [];
+
+ foreach ($font['C'] as $num => $d) {
+ if (intval($num) > 0 || $num == '0') {
+ if (!$font['isUnicode']) {
+ // With Unicode, widths array isn't used
+ if ($lastChar > 0 && $num > $lastChar + 1) {
+ for ($i = $lastChar + 1; $i < $num; $i++) {
+ $widths[] = 0;
+ }
+ }
+ }
+
+ $widths[] = $d;
+
+ if ($font['isUnicode']) {
+ $cid_widths[$num] = $d;
+ }
+
+ if ($firstChar == -1) {
+ $firstChar = $num;
+ }
+
+ $lastChar = $num;
+ }
+ }
+
+ // also need to adjust the widths for the differences array
+ if (isset($object['differences'])) {
+ foreach ($object['differences'] as $charNum => $charName) {
+ if ($charNum > $lastChar) {
+ if (!$object['isUnicode']) {
+ // With Unicode, widths array isn't used
+ for ($i = $lastChar + 1; $i <= $charNum; $i++) {
+ $widths[] = 0;
+ }
+ }
+
+ $lastChar = $charNum;
+ }
+
+ if (isset($font['C'][$charName])) {
+ $widths[$charNum - $firstChar] = $font['C'][$charName];
+ if ($font['isUnicode']) {
+ $cid_widths[$charName] = $font['C'][$charName];
+ }
+ }
+ }
+ }
+
+ if ($font['isUnicode']) {
+ $font['CIDWidths'] = $cid_widths;
+ }
+
+ $this->addMessage('selectFont: FirstChar = ' . $firstChar);
+ $this->addMessage('selectFont: LastChar = ' . $lastChar);
+
+ $widthid = -1;
+
+ if (!$font['isUnicode']) {
+ // With Unicode, widths array isn't used
+
+ $this->numObj++;
+ $this->o_contents($this->numObj, 'new', 'raw');
+ $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
+ $widthid = $this->numObj;
+ }
+
+ $missing_width = 500;
+ $stemV = 70;
+
+ if (isset($font['MissingWidth'])) {
+ $missing_width = $font['MissingWidth'];
+ }
+ if (isset($font['StdVW'])) {
+ $stemV = $font['StdVW'];
+ } else {
+ if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
+ $stemV = 120;
+ }
+ }
+
+ // load the pfb file, and put that into an object too.
+ // note that pdf supports only binary format type 1 font files, though there is a
+ // simple utility to convert them from pfa to pfb.
+ $data = file_get_contents($fbfile);
+
+ // create the font descriptor
+ $this->numObj++;
+ $fontDescriptorId = $this->numObj;
+
+ $this->numObj++;
+ $pfbid = $this->numObj;
+
+ // determine flags (more than a little flakey, hopefully will not matter much)
+ $flags = 0;
+
+ if ($font['ItalicAngle'] != 0) {
+ $flags += pow(2, 6);
+ }
+
+ if ($font['IsFixedPitch'] === 'true') {
+ $flags += 1;
+ }
+
+ $flags += pow(2, 5); // assume non-sybolic
+ $list = [
+ 'Ascent' => 'Ascender',
+ 'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
+ 'MissingWidth' => 'MissingWidth',
+ 'Descent' => 'Descender',
+ 'FontBBox' => 'FontBBox',
+ 'ItalicAngle' => 'ItalicAngle'
+ ];
+ $fdopt = [
+ 'Flags' => $flags,
+ 'FontName' => $adobeFontName,
+ 'StemV' => $stemV
+ ];
+
+ foreach ($list as $k => $v) {
+ if (isset($font[$v])) {
+ $fdopt[$k] = $font[$v];
+ }
+ }
+
+ if ($isPfbFont) {
+ $fdopt['FontFile'] = $pfbid;
+ } elseif ($isTtfFont) {
+ $fdopt['FontFile2'] = $pfbid;
+ }
+
+ $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
+
+ // embed the font program
+ $this->o_contents($this->numObj, 'new');
+ $this->objects[$pfbid]['c'] .= $data;
+
+ // determine the cruicial lengths within this file
+ if ($isPfbFont) {
+ $l1 = strpos($data, 'eexec') + 6;
+ $l2 = strpos($data, '00000000') - $l1;
+ $l3 = mb_strlen($data, '8bit') - $l2 - $l1;
+ $this->o_contents(
+ $this->numObj,
+ 'add',
+ ['Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3]
+ );
+ } elseif ($isTtfFont) {
+ $l1 = mb_strlen($data, '8bit');
+ $this->o_contents($this->numObj, 'add', ['Length1' => $l1]);
+ }
+
+ // tell the font object about all this new stuff
+ $options = [
+ 'BaseFont' => $adobeFontName,
+ 'MissingWidth' => $missing_width,
+ 'Widths' => $widthid,
+ 'FirstChar' => $firstChar,
+ 'LastChar' => $lastChar,
+ 'FontDescriptor' => $fontDescriptorId
+ ];
+
+ if ($isTtfFont) {
+ $options['SubType'] = 'TrueType';
+ }
+
+ $this->addMessage("adding extra info to font.($fontObjId)");
+
+ foreach ($options as $fk => $fv) {
+ $this->addMessage("$fk : $fv");
+ }
+ }
+
+ return $options;
+ }
+
/**
* A toUnicode section, needed for unicode fonts
*
@@ -962,20 +1273,20 @@ class Cpdf
{
switch ($action) {
case 'new':
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'toUnicode'
- );
+ ];
break;
case 'add':
break;
case 'out':
- $ordering = '(UCS)';
- $registry = '(Adobe)';
+ $ordering = 'UCS';
+ $registry = 'Adobe';
if ($this->encrypted) {
$this->encryptInit($id);
$ordering = $this->ARC4($ordering);
- $registry = $this->ARC4($registry);
+ $registry = $this->filterText($this->ARC4($registry), false, false);
}
$stream = <<> def
/CMapName /Adobe-Identity-UCS def
@@ -1027,7 +1338,7 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
+ $this->objects[$id] = ['t' => 'fontDescriptor', 'info' => $options];
break;
case 'out':
@@ -1093,7 +1404,7 @@ EOT;
switch ($action) {
case 'new':
// the options array should contain 'differences' and maybe 'encoding'
- $this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
+ $this->objects[$id] = ['t' => 'fontEncoding', 'info' => $options];
break;
case 'out':
@@ -1145,7 +1456,7 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
+ $this->objects[$id] = ['t' => 'fontDescendentCID', 'info' => $options];
// we need a CID system info section
$cidSystemInfoId = ++$this->numObj;
@@ -1231,15 +1542,15 @@ EOT;
{
switch ($action) {
case 'new':
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'cidSystemInfo'
- );
+ ];
break;
case 'add':
break;
case 'out':
- $ordering = '(UCS)';
- $registry = '(Adobe)';
+ $ordering = 'UCS';
+ $registry = 'Adobe';
if ($this->encrypted) {
$this->encryptInit($id);
@@ -1250,12 +1561,12 @@ EOT;
$res = "\n$id 0 obj\n";
- $res .= '<>";
- $res .= "\nendobj";;
+ $res .= "\nendobj";
return $res;
}
@@ -1279,7 +1590,7 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
+ $this->objects[$id] = ['t' => 'fontGIDtoCIDMap', 'info' => $options];
break;
case 'out':
@@ -1336,7 +1647,7 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
+ $this->objects[$id] = ['t' => 'procset', 'info' => ['PDF' => 1, 'Text' => 1]];
$this->o_pages($this->currentNode, 'procset', $id);
$this->procsetObjectId = $id;
break;
@@ -1380,13 +1691,13 @@ EOT;
case 'new':
$this->infoObject = $id;
$date = 'D:' . @date('Ymd');
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'info',
- 'info' => array(
+ 'info' => [
'Producer' => 'CPDF (dompdf)',
'CreationDate' => $date
- )
- );
+ ]
+ ];
break;
case 'Title':
case 'Author':
@@ -1449,10 +1760,10 @@ EOT;
switch ($action) {
case 'new':
if (is_array($options)) {
- $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
+ $this->objects[$id] = ['t' => 'action', 'info' => $options, 'type' => $options['type']];
} else {
// then assume a URI action
- $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
+ $this->objects[$id] = ['t' => 'action', 'info' => $options, 'type' => 'URI'];
}
break;
@@ -1516,7 +1827,7 @@ EOT;
// and add the action object which is going to be required
switch ($options['type']) {
case 'link':
- $this->objects[$id] = array('t' => 'annotation', 'info' => $options);
+ $this->objects[$id] = ['t' => 'annotation', 'info' => $options];
$this->numObj++;
$this->o_action($this->numObj, 'new', $options['url']);
$this->objects[$id]['info']['actionId'] = $this->numObj;
@@ -1525,9 +1836,9 @@ EOT;
case 'ilink':
// this is to a named internal link
$label = $options['label'];
- $this->objects[$id] = array('t' => 'annotation', 'info' => $options);
+ $this->objects[$id] = ['t' => 'annotation', 'info' => $options];
$this->numObj++;
- $this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
+ $this->o_action($this->numObj, 'new', ['type' => 'ilink', 'label' => $label]);
$this->objects[$id]['info']['actionId'] = $this->numObj;
break;
}
@@ -1576,14 +1887,14 @@ EOT;
switch ($action) {
case 'new':
$this->numPages++;
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'page',
- 'info' => array(
+ 'info' => [
'parent' => $this->currentNode,
'pageNum' => $this->numPages,
'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox']
- )
- );
+ ]
+ ];
if (is_array($options)) {
// then this must be a page insertion, array should contain 'rid','pos'=[before|after]
@@ -1598,7 +1909,7 @@ EOT;
$this->numObj++;
$this->o_contents($this->numObj, 'new', $id);
$this->currentContents = $this->numObj;
- $this->objects[$id]['info']['contents'] = array();
+ $this->objects[$id]['info']['contents'] = [];
$this->objects[$id]['info']['contents'][] = $this->numObj;
$match = ($this->numPages % 2 ? 'odd' : 'even');
@@ -1616,7 +1927,7 @@ EOT;
case 'annot':
// add an annotation to this page
if (!isset($o['info']['annot'])) {
- $o['info']['annot'] = array();
+ $o['info']['annot'] = [];
}
// $options should contain the id of the annotation dictionary
@@ -1686,7 +1997,7 @@ EOT;
switch ($action) {
case 'new':
- $this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
+ $this->objects[$id] = ['t' => 'contents', 'c' => '', 'info' => []];
if (mb_strlen($options, '8bit') && intval($options)) {
// then this contents is the primary for a page
$this->objects[$id]['onPage'] = $options;
@@ -1747,12 +2058,12 @@ EOT;
{
switch ($action) {
case 'new':
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'embedjs',
- 'info' => array(
+ 'info' => [
'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]'
- )
- );
+ ]
+ ];
break;
case 'out':
@@ -1779,13 +2090,13 @@ EOT;
{
switch ($action) {
case 'new':
- $this->objects[$id] = array(
+ $this->objects[$id] = [
't' => 'javascript',
- 'info' => array(
+ 'info' => [
'S' => '/JavaScript',
'JS' => '(' . $this->filterText($code, true, false) . ')',
- )
- );
+ ]
+ ];
break;
case 'out':
@@ -1816,7 +2127,7 @@ EOT;
switch ($action) {
case 'new':
// make the new object
- $this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
+ $this->objects[$id] = ['t' => 'image', 'data' => &$options['data'], 'info' => []];
$info =& $this->objects[$id]['info'];
@@ -1835,10 +2146,10 @@ EOT;
}
switch ($options['channels']) {
- case 1:
+ case 1:
$info['ColorSpace'] = '/DeviceGray';
break;
- case 4:
+ case 4:
$info['ColorSpace'] = '/DeviceCMYK';
break;
default:
@@ -1917,7 +2228,7 @@ EOT;
// assign it a place in the named resource dictionary as an external object, according to
// the label passed in with it.
- $this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
+ $this->o_pages($this->currentNode, 'xObject', ['label' => $options['label'], 'objNum' => $id]);
// also make sure that we have the right procset object for it.
$this->o_procset($this->procsetObjectId, 'add', 'ImageC');
@@ -1955,7 +2266,7 @@ EOT;
*/
protected function o_extGState($id, $action, $options = "")
{
- static $valid_params = array(
+ static $valid_params = [
"LW",
"LC",
"LC",
@@ -1982,15 +2293,15 @@ EOT;
"ca",
"AIS",
"TK"
- );
+ ];
switch ($action) {
case "new":
- $this->objects[$id] = array('t' => 'extGState', 'info' => $options);
+ $this->objects[$id] = ['t' => 'extGState', 'info' => $options];
// Tell the pages about the new resource
$this->numStates++;
- $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
+ $this->o_pages($this->currentNode, 'extGState', ["objNum" => $id, "stateNum" => $this->numStates]);
break;
case "out":
@@ -2012,6 +2323,362 @@ EOT;
return null;
}
+ /**
+ * @param integer $id
+ * @param string $action
+ * @param mixed $options
+ * @return string
+ */
+ protected function o_xobject($id, $action, $options = '')
+ {
+ switch ($action) {
+ case 'new':
+ $this->objects[$id] = ['t' => 'xobject', 'info' => $options, 'c' => ''];
+ break;
+
+ case 'procset':
+ $this->objects[$id]['procset'] = $options;
+ break;
+
+ case 'font':
+ $this->objects[$id]['fonts'][$options['fontNum']] = [
+ 'objNum' => $options['objNum'],
+ 'fontNum' => $options['fontNum']
+ ];
+ break;
+
+ case 'xObject':
+ $this->objects[$id]['xObjects'][] = ['objNum' => $options['objNum'], 'label' => $options['label']];
+ break;
+
+ case 'out':
+ $o = &$this->objects[$id];
+ $res = "\n$id 0 obj\n<< /Type /XObject\n";
+
+ foreach ($o["info"] as $k => $v) {
+ switch($k)
+ {
+ case 'Subtype':
+ $res .= "/Subtype /$v\n";
+ break;
+ case 'bbox':
+ $res .= "/BBox [";
+ foreach ($v as $value) {
+ $res .= sprintf("%.4F ", $value);
+ }
+ $res .= "]\n";
+ break;
+ default:
+ $res .= "/$k $v\n";
+ break;
+ }
+ }
+ $res .= "/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]\n";
+
+ $res .= "/Resources <<";
+ if (isset($o['procset'])) {
+ $res .= "\n/ProcSet " . $o['procset'] . " 0 R";
+ } else {
+ $res .= "\n/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]";
+ }
+ if (isset($o['fonts']) && count($o['fonts'])) {
+ $res .= "\n/Font << ";
+ foreach ($o['fonts'] as $finfo) {
+ $res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
+ }
+ $res .= "\n>>";
+ }
+ if (isset($o['xObjects']) && count($o['xObjects'])) {
+ $res .= "\n/XObject << ";
+ foreach ($o['xObjects'] as $finfo) {
+ $res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
+ }
+ $res .= "\n>>";
+ }
+ $res .= "\n>>\n";
+
+ $tmp = $o["c"];
+ if ($this->compressionReady && $this->options['compression']) {
+ // then implement ZLIB based compression on this content stream
+ $res .= " /Filter /FlateDecode\n";
+ $tmp = gzcompress($tmp, 6);
+ }
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $tmp = $this->ARC4($tmp);
+ }
+
+ $res .= "/Length " . mb_strlen($tmp, '8bit') . " >>\n";
+ $res .= "stream\n" . $tmp . "\nendstream" . "\nendobj";;
+
+ return $res;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
+ */
+ protected function o_acroform($id, $action, $options = '')
+ {
+ switch ($action) {
+ case "new":
+ $this->o_catalog($this->catalogId, 'acroform', $id);
+ $this->objects[$id] = array('t' => 'acroform', 'info' => $options);
+ break;
+
+ case 'addfield':
+ $this->objects[$id]['info']['Fields'][] = $options;
+ break;
+
+ case 'font':
+ $this->objects[$id]['fonts'][$options['fontNum']] = [
+ 'objNum' => $options['objNum'],
+ 'fontNum' => $options['fontNum']
+ ];
+ break;
+
+ case "out":
+ $o = &$this->objects[$id];
+ $res = "\n$id 0 obj\n<<";
+
+ foreach ($o["info"] as $k => $v) {
+ switch($k) {
+ case 'Fields':
+ $res .= " /Fields [";
+ foreach ($v as $i) {
+ $res .= "$i 0 R ";
+ }
+ $res .= "]\n";
+ break;
+ default:
+ $res .= "/$k $v\n";
+ }
+ }
+
+ $res .= "/DR <<\n";
+ if (isset($o['fonts']) && count($o['fonts'])) {
+ $res .= "/Font << \n";
+ foreach ($o['fonts'] as $finfo) {
+ $res .= "/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R\n";
+ }
+ $res .= ">>\n";
+ }
+ $res .= ">>\n";
+
+ $res .= ">>\nendobj";
+
+ return $res;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $id
+ * @param $action
+ * @param mixed $options
+ * @return null|string
+ */
+ protected function o_field($id, $action, $options = '')
+ {
+ switch ($action) {
+ case "new":
+ $this->o_page($options['pageid'], 'annot', $id);
+ $this->o_acroform($this->acroFormId, 'addfield', $id);
+ $this->objects[$id] = ['t' => 'field', 'info' => $options];
+ break;
+
+ case 'set':
+ $this->objects[$id]['info'] = array_merge($this->objects[$id]['info'], $options);
+ break;
+
+ case "out":
+ $o = &$this->objects[$id];
+ $res = "\n$id 0 obj\n<< /Type /Annot /Subtype /Widget \n";
+
+ $encrypted = $this->encrypted;
+ if ($encrypted) {
+ $this->encryptInit($id);
+ }
+
+ foreach ($o["info"] as $k => $v) {
+ switch ($k) {
+ case 'pageid':
+ $res .= "/P $v 0 R\n";
+ break;
+ case 'value':
+ if ($encrypted) {
+ $v = $this->filterText($this->ARC4($v), false, false);
+ }
+ $res .= "/V ($v)\n";
+ break;
+ case 'refvalue':
+ $res .= "/V $v 0 R\n";
+ break;
+ case 'da':
+ if ($encrypted) {
+ $v = $this->filterText($this->ARC4($v), false, false);
+ }
+ $res .= "/DA ($v)\n";
+ break;
+ case 'options':
+ $res .= "/Opt [\n";
+ foreach ($v as $opt) {
+ if ($encrypted) {
+ $opt = $this->filterText($this->ARC4($opt), false, false);
+ }
+ $res .= "($opt)\n";
+ }
+ $res .= "]\n";
+ break;
+ case 'rect':
+ $res .= "/Rect [";
+ foreach ($v as $value) {
+ $res .= sprintf("%.4F ", $value);
+ }
+ $res .= "]\n";
+ break;
+ case 'appearance':
+ $res .= "/AP << ";
+ foreach ($v as $a => $ref) {
+ $res .= "/$a $ref 0 R ";
+ }
+ $res .= ">>\n";
+ break;
+ case 'T':
+ if($encrypted) {
+ $v = $this->filterText($this->ARC4($v), false, false);
+ }
+ $res .= "/T ($v)\n";
+ break;
+ default:
+ $res .= "/$k $v\n";
+ }
+
+ }
+
+ $res .= ">>\nendobj";
+
+ return $res;
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ * @param $id
+ * @param $action
+ * @param string $options
+ * @return null|string
+ */
+ protected function o_sig($id, $action, $options = '')
+ {
+ $sign_maxlen = $this->signatureMaxLen;
+
+ switch ($action) {
+ case "new":
+ $this->objects[$id] = array('t' => 'sig', 'info' => $options);
+ $this->byteRange[$id] = ['t' => 'sig'];
+ break;
+
+ case 'byterange':
+ $o = &$this->objects[$id];
+ $content =& $options['content'];
+ $content_len = strlen($content);
+ $pos = strpos($content, sprintf("/ByteRange [ %'.010d", $id));
+ $len = strlen('/ByteRange [ ********** ********** ********** ********** ]');
+ $rangeStartPos = $pos + $len + 1 + 10; // before '<'
+ $content = substr_replace($content, str_pad(sprintf('/ByteRange [ 0 %u %u %u ]', $rangeStartPos, $rangeStartPos + $sign_maxlen + 2, $content_len - 2 - $sign_maxlen - $rangeStartPos ), $len, ' ', STR_PAD_RIGHT), $pos, $len);
+
+ $fuid = uniqid();
+ $tmpInput = $this->tmp . "/pkcs7.tmp." . $fuid . '.in';
+ $tmpOutput = $this->tmp . "/pkcs7.tmp." . $fuid . '.out';
+
+ if (file_put_contents($tmpInput, substr($content, 0, $rangeStartPos)) === false) {
+ throw new \Exception("Unable to write temporary file for signing.");
+ }
+ if (file_put_contents($tmpInput, substr($content, $rangeStartPos + 2 + $sign_maxlen),
+ FILE_APPEND) === false) {
+ throw new \Exception("Unable to write temporary file for signing.");
+ }
+
+ if (openssl_pkcs7_sign($tmpInput, $tmpOutput,
+ $o['info']['SignCert'],
+ array($o['info']['PrivKey'], $o['info']['Password']),
+ array(), PKCS7_BINARY | PKCS7_DETACHED) === false) {
+ throw new \Exception("Failed to prepare signature.");
+ }
+
+ $signature = file_get_contents($tmpOutput);
+
+ unlink($tmpInput);
+ unlink($tmpOutput);
+
+ $sign = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
+ list($head, $signature) = explode("\n\n", $sign);
+
+ $signature = base64_decode(trim($signature));
+
+ $signature = current(unpack('H*', $signature));
+ $signature = str_pad($signature, $sign_maxlen, '0');
+ $siglen = strlen($signature);
+ if (strlen($signature) > $sign_maxlen) {
+ throw new \Exception("Signature length ($siglen) exceeds the $sign_maxlen limit.");
+ }
+
+ $content = substr_replace($content, $signature, $rangeStartPos + 1, $sign_maxlen);
+ break;
+
+ case "out":
+ $res = "\n$id 0 obj\n<<\n";
+
+ $encrypted = $this->encrypted;
+ if ($encrypted) {
+ $this->encryptInit($id);
+ }
+
+ $res .= "/ByteRange " .sprintf("[ %'.010d ********** ********** ********** ]\n", $id);
+ $res .= "/Contents <" . str_pad('', $sign_maxlen, '0') . ">\n";
+ $res .= "/Filter/Adobe.PPKLite\n"; //PPKMS \n";
+ $res .= "/Type/Sig/SubFilter/adbe.pkcs7.detached \n";
+
+ $date = "D:" . substr_replace(date('YmdHisO'), '\'', -2, 0) . '\'';
+ if ($encrypted) {
+ $date = $this->ARC4($date);
+ }
+
+ $res .= "/M ($date)\n";
+ $res .= "/Prop_Build << /App << /Name /DomPDF >> /Filter << /Name /Adobe.PPKLite >> >>\n";
+
+ $o = &$this->objects[$id];
+ foreach ($o['info'] as $k => $v) {
+ switch($k) {
+ case 'Name':
+ case 'Location':
+ case 'Reason':
+ case 'ContactInfo':
+ if ($v !== null && $v !== '') {
+ $res .= "/$k (" .
+ ($encrypted ? $this->filterText($this->ARC4($v), false, false) : $v) . ") \n";
+ }
+ break;
+ }
+ }
+ $res .= ">>\nendobj";
+
+ return $res;
+ }
+
+ return null;
+ }
+
/**
* encryption object.
*
@@ -2025,7 +2692,7 @@ EOT;
switch ($action) {
case 'new':
// make the new object
- $this->objects[$id] = array('t' => 'encryption', 'info' => $options);
+ $this->objects[$id] = ['t' => 'encryption', 'info' => $options];
$this->arc4_objnum = $id;
break;
@@ -2101,6 +2768,154 @@ EOT;
return null;
}
+ protected function o_indirect_references($id, $action, $options = null)
+ {
+ switch ($action) {
+ case 'new':
+ case 'add':
+ if ($id === 0) {
+ $id = ++$this->numObj;
+ $this->o_catalog($this->catalogId, 'names', $id);
+ $this->objects[$id] = ['t' => 'indirect_references', 'info' => $options];
+ $this->indirectReferenceId = $id;
+ } else {
+ $this->objects[$id]['info'] = array_merge($this->objects[$id]['info'], $options);
+ }
+ break;
+ case 'out':
+ $res = "\n$id 0 obj << ";
+
+ foreach($this->objects[$id]['info'] as $referenceObjName => $referenceObjId) {
+ $res .= "/$referenceObjName $referenceObjId 0 R ";
+ }
+
+ $res .= ">> endobj";
+ return $res;
+ }
+
+ return null;
+ }
+
+ protected function o_names($id, $action, $options = null)
+ {
+ switch ($action) {
+ case 'new':
+ case 'add':
+ if ($id === 0) {
+ $id = ++$this->numObj;
+ $this->objects[$id] = ['t' => 'names', 'info' => [$options]];
+ $this->o_indirect_references($this->indirectReferenceId, 'add', ['EmbeddedFiles' => $id]);
+ $this->embeddedFilesId = $id;
+ } else {
+ $this->objects[$id]['info'][] = $options;
+ }
+ break;
+ case 'out':
+ $info = &$this->objects[$id]['info'];
+ $res = '';
+ if (count($info) > 0) {
+ $res = "\n$id 0 obj << /Names [ ";
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ }
+
+ foreach ($info as $entry) {
+ if ($this->encrypted) {
+ $filename = $this->ARC4($entry['filename']);
+ } else {
+ $filename = $entry['filename'];
+ }
+
+ $res .= "($filename) " . $entry['dict_reference'] . " 0 R ";
+ }
+
+ $res .= "] >> endobj";
+ }
+ return $res;
+ }
+
+ return null;
+ }
+
+ protected function o_embedded_file_dictionary($id, $action, $options = null)
+ {
+ switch ($action) {
+ case 'new':
+ $embeddedFileId = ++$this->numObj;
+ $options['embedded_reference'] = $embeddedFileId;
+ $this->objects[$id] = ['t' => 'embedded_file_dictionary', 'info' => $options];
+ $this->o_embedded_file($embeddedFileId, 'new', $options);
+ $options['dict_reference'] = $id;
+ $this->o_names($this->embeddedFilesId, 'add', $options);
+ break;
+ case 'out':
+ $info = &$this->objects[$id]['info'];
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $filename = $this->ARC4($info['filename']);
+ $description = $this->ARC4($info['description']);
+ } else {
+ $filename = $info['filename'];
+ $description = $info['description'];
+ }
+
+ $res = "\n$id 0 obj <>";
+ $res .= " /F ($filename) /UF ($filename) /Desc ($description)";
+ $res .= " >> endobj";
+ return $res;
+ }
+
+ return null;
+ }
+
+ protected function o_embedded_file($id, $action, $options = null): ?string
+ {
+ switch ($action) {
+ case 'new':
+ $this->objects[$id] = ['t' => 'embedded_file', 'info' => $options];
+ break;
+ case 'out':
+ $info = &$this->objects[$id]['info'];
+
+ if ($this->compressionReady) {
+ $filepath = $info['filepath'];
+ $checksum = md5_file($filepath);
+ $f = fopen($filepath, "rb");
+
+ $file_content_compressed = '';
+ $deflateContext = deflate_init(ZLIB_ENCODING_DEFLATE, ['level' => 6]);
+ while (($block = fread($f, 8192))) {
+ $file_content_compressed .= deflate_add($deflateContext, $block, ZLIB_NO_FLUSH);
+ }
+ $file_content_compressed .= deflate_add($deflateContext, '', ZLIB_FINISH);
+ $file_size_uncompressed = ftell($f);
+ fclose($f);
+ } else {
+ $file_content = file_get_contents($info['filepath']);
+ $file_size_uncompressed = mb_strlen($file_content, '8bit');
+ $checksum = md5($file_content);
+ }
+
+ if ($this->encrypted) {
+ $this->encryptInit($id);
+ $checksum = $this->ARC4($checksum);
+ $file_content_compressed = $this->ARC4($file_content_compressed);
+ }
+ $file_size_compressed = mb_strlen($file_content_compressed, '8bit');
+
+ $res = "\n$id 0 obj <>" .
+ " /Type/EmbeddedFile /Filter/FlateDecode" .
+ " /Length $file_size_compressed >> stream\n$file_content_compressed\nendstream\nendobj";
+
+ return $res;
+ }
+
+ return null;
+ }
+
/**
* ARC4 functions
* A series of function to implement ARC4 encoding in PHP
@@ -2221,7 +3036,7 @@ EOT;
function addLink($url, $x0, $y0, $x1, $y1)
{
$this->numObj++;
- $info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
+ $info = ['type' => 'link', 'url' => $url, 'rect' => [$x0, $y0, $x1, $y1]];
$this->o_annotation($this->numObj, 'new', $info);
}
@@ -2237,7 +3052,7 @@ EOT;
function addInternalLink($label, $x0, $y0, $x1, $y1)
{
$this->numObj++;
- $info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
+ $info = ['type' => 'ilink', 'label' => $label, 'rect' => [$x0, $y0, $x1, $y1]];
$this->o_annotation($this->numObj, 'new', $info);
}
@@ -2250,11 +3065,11 @@ EOT;
* @param string $ownerPass
* @param array $pc
*/
- function setEncryption($userPass = '', $ownerPass = '', $pc = array())
+ function setEncryption($userPass = '', $ownerPass = '', $pc = [])
{
$p = bindec("11000000");
- $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
+ $options = ['print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32];
foreach ($pc as $k => $v) {
if ($v && isset($options[$k])) {
@@ -2274,7 +3089,7 @@ EOT;
$ownerPass = $userPass;
}
- $this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
+ $this->o_encryption($this->numObj, 'new', ['user' => $userPass, 'owner' => $ownerPass, 'p' => $p]);
}
}
@@ -2307,11 +3122,11 @@ EOT;
$id = $this->catalogId;
- $this->o_catalog($id, 'javascript', $js_id);
+ $this->o_indirect_references($this->indirectReferenceId, 'add', ['JavaScript' => $js_id]);
}
if ($this->fileIdentifier === '') {
- $tmp = implode('', $this->objects[$this->infoObject]['info']);
+ $tmp = implode('', $this->objects[$this->infoObject]['info']);
$this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand());
}
@@ -2322,10 +3137,17 @@ EOT;
$this->checkAllHere();
- $xref = array();
- $content = '%PDF-1.3';
+ $xref = [];
+ $content = '%PDF-' . self::PDF_VERSION;
$pos = mb_strlen($content, '8bit');
+ // pre-process o_font objects before output of all objects
+ foreach ($this->objects as $k => $v) {
+ if ($v['t'] === 'font') {
+ $this->o_font($k, 'add');
+ }
+ }
+
foreach ($this->objects as $k => $v) {
$tmp = 'o_' . $v['t'];
$cont = $this->$tmp($k, 'out');
@@ -2358,6 +3180,13 @@ EOT;
$content .= ">>\nstartxref\n$pos\n%%EOF\n";
+ if (count($this->byteRange) > 0) {
+ foreach ($this->byteRange as $k => $v) {
+ $tmp = 'o_' . $v['t'];
+ $this->$tmp($k, 'byterange', ['content' => &$content]);
+ }
+ }
+
return $content;
}
@@ -2368,10 +3197,10 @@ EOT;
*
* @param array $pageSize
*/
- private function newDocument($pageSize = array(0, 0, 612, 792))
+ private function newDocument($pageSize = [0, 0, 612, 792])
{
$this->numObj = 0;
- $this->objects = array();
+ $this->objects = [];
$this->numObj++;
$this->o_catalog($this->numObj, 'new');
@@ -2420,7 +3249,7 @@ EOT;
}
//$name filename without folder and extension of font metrics
- //$dir folder of font metrics
+ //$dir folder of font metrics
//$fontcache folder of runtime created php serialized version of font metrics.
// If this is not given, the same folder as the font metrics will be used.
// Storing and reusing serialized versions improves speed much
@@ -2463,10 +3292,10 @@ EOT;
if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
// then rebuild the php_.afm file from the .afm file
$this->addMessage("openFont: build php file from $dir$metrics_name");
- $data = array();
+ $data = [];
// 20 => 'space'
- $data['codeToName'] = array();
+ $data['codeToName'] = [];
// Since we're not going to enable Unicode for the core fonts we need to use a font-based
// setting for Unicode support rather than a global setting.
@@ -2517,7 +3346,7 @@ EOT;
//C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
case 'C': // Found in AFM files
$bits = explode(';', trim($row));
- $dtmp = array();
+ $dtmp = ['C' => null, 'N' => null, 'WX' => null, 'B' => []];
foreach ($bits as $bit) {
$bits2 = explode(' ', trim($bit));
@@ -2526,7 +3355,7 @@ EOT;
}
if (count($bits2) > 2) {
- $dtmp[$bits2[0]] = array();
+ $dtmp[$bits2[0]] = [];
for ($i = 1; $i < count($bits2); $i++) {
$dtmp[$bits2[0]][] = $bits2[$i];
}
@@ -2542,15 +3371,15 @@ EOT;
$width = floatval($dtmp['WX']);
if ($c >= 0) {
- if ($c != hexdec($n)) {
+ if (!ctype_xdigit($n) || $c != hexdec($n)) {
$data['codeToName'][$c] = $n;
}
$data['C'][$c] = $width;
- } else {
+ } elseif (isset($n)) {
$data['C'][$n] = $width;
}
- if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
+ if (!isset($data['MissingWidth']) && $c === -1 && $n === '.notdef') {
$data['MissingWidth'] = $width;
}
@@ -2563,7 +3392,7 @@ EOT;
}
$bits = explode(';', trim($row));
- $dtmp = array();
+ $dtmp = ['G' => null, 'N' => null, 'U' => null, 'WX' => null];
foreach ($bits as $bit) {
$bits2 = explode(' ', trim($bit));
@@ -2572,7 +3401,7 @@ EOT;
}
if (count($bits2) > 2) {
- $dtmp[$bits2[0]] = array();
+ $dtmp[$bits2[0]] = [];
for ($i = 1; $i < count($bits2); $i++) {
$dtmp[$bits2[0]][] = $bits2[$i];
}
@@ -2595,15 +3424,15 @@ EOT;
$cidtogid[$c * 2 + 1] = chr($glyph & 0xFF);
}
- if ($c != hexdec($n)) {
+ if (!ctype_xdigit($n) || $c != hexdec($n)) {
$data['codeToName'][$c] = $n;
}
$data['C'][$c] = $width;
- } else {
+ } elseif (isset($n)) {
$data['C'][$n] = $width;
}
- if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
+ if (!isset($data['MissingWidth']) && $c === -1 && $n === '.notdef') {
$data['MissingWidth'] = $width;
}
@@ -2653,10 +3482,16 @@ EOT;
* @param $fontName
* @param string $encoding
* @param bool $set
+ * @param bool $isSubsetting
* @return int
+ * @throws FontNotFoundException
*/
- function selectFont($fontName, $encoding = '', $set = true)
+ function selectFont($fontName, $encoding = '', $set = true, $isSubsetting = true)
{
+ if ($fontName === null || $fontName === '') {
+ return $this->currentFontNum;
+ }
+
$ext = substr($fontName, -4);
if ($ext === '.afm' || $ext === '.ufm') {
$fontName = substr($fontName, 0, mb_strlen($fontName) - 4);
@@ -2675,8 +3510,7 @@ EOT;
$font = &$this->fonts[$fontName];
$name = basename($fontName);
- $dir = dirname($fontName) . '/';
- $options = array('name' => $name, 'fontFileName' => $fontName);
+ $options = ['name' => $name, 'fontFileName' => $fontName, 'isSubsetting' => $isSubsetting];
if (is_array($encoding)) {
// then encoding and differences might be set
@@ -2694,286 +3528,25 @@ EOT;
}
}
- $fontObj = $this->numObj;
$this->o_font($this->numObj, 'new', $options);
- $font['fontNum'] = $this->numFonts;
- // if this is a '.afm' font, and there is a '.pfa' file to go with it (as there
- // should be for all non-basic fonts), then load it into an object and put the
- // references into the font object
- $basefile = $fontName;
-
- $fbtype = '';
- if (file_exists("$basefile.ttf")) {
- $fbtype = 'ttf';
- } elseif (file_exists("$basefile.TTF")) {
- $fbtype = 'TTF';
- } elseif (file_exists("$basefile.pfb")) {
- $fbtype = 'pfb';
- } elseif (file_exists("$basefile.PFB")) {
- $fbtype = 'PFB';
- }
-
- $fbfile = "$basefile.$fbtype";
-
- // $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
- // $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
- $this->addMessage('selectFont: checking for - ' . $fbfile);
-
- // OAR - I don't understand this old check
- // if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
- if ($fbtype) {
- $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
- // $fontObj = $this->numObj;
- $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
-
- // find the array of font widths, and put that into an object.
- $firstChar = -1;
- $lastChar = 0;
- $widths = array();
- $cid_widths = array();
-
- foreach ($font['C'] as $num => $d) {
- if (intval($num) > 0 || $num == '0') {
- if (!$font['isUnicode']) {
- // With Unicode, widths array isn't used
- if ($lastChar > 0 && $num > $lastChar + 1) {
- for ($i = $lastChar + 1; $i < $num; $i++) {
- $widths[] = 0;
- }
- }
- }
-
- $widths[] = $d;
-
- if ($font['isUnicode']) {
- $cid_widths[$num] = $d;
- }
-
- if ($firstChar == -1) {
- $firstChar = $num;
- }
-
- $lastChar = $num;
- }
- }
-
- // also need to adjust the widths for the differences array
- if (isset($options['differences'])) {
- foreach ($options['differences'] as $charNum => $charName) {
- if ($charNum > $lastChar) {
- if (!$font['isUnicode']) {
- // With Unicode, widths array isn't used
- for ($i = $lastChar + 1; $i <= $charNum; $i++) {
- $widths[] = 0;
- }
- }
-
- $lastChar = $charNum;
- }
-
- if (isset($font['C'][$charName])) {
- $widths[$charNum - $firstChar] = $font['C'][$charName];
- if ($font['isUnicode']) {
- $cid_widths[$charName] = $font['C'][$charName];
- }
- }
- }
- }
-
- if ($font['isUnicode']) {
- $font['CIDWidths'] = $cid_widths;
- }
-
- $this->addMessage('selectFont: FirstChar = ' . $firstChar);
- $this->addMessage('selectFont: LastChar = ' . $lastChar);
-
- $widthid = -1;
-
- if (!$font['isUnicode']) {
- // With Unicode, widths array isn't used
-
- $this->numObj++;
- $this->o_contents($this->numObj, 'new', 'raw');
- $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
- $widthid = $this->numObj;
- }
-
- $missing_width = 500;
- $stemV = 70;
-
- if (isset($font['MissingWidth'])) {
- $missing_width = $font['MissingWidth'];
- }
- if (isset($font['StdVW'])) {
- $stemV = $font['StdVW'];
- } else {
- if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
- $stemV = 120;
- }
- }
-
- // load the pfb file, and put that into an object too.
- // note that pdf supports only binary format type 1 font files, though there is a
- // simple utility to convert them from pfa to pfb.
- // FIXME: should we move font subset creation to CPDF::output? See notes in issue #750.
- if (!$this->isUnicode || strtolower($fbtype) !== 'ttf' || empty($this->stringSubsets)) {
- $data = file_get_contents($fbfile);
- } else {
- $this->stringSubsets[$fontName][] = 32; // Force space if not in yet
-
- $subset = $this->stringSubsets[$fontName];
- sort($subset);
-
- // Load font
- $font_obj = Font::load($fbfile);
- $font_obj->parse();
-
- // Define subset
- $font_obj->setSubset($subset);
- $font_obj->reduce();
-
- // Write new font
- $tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid();
- $font_obj->open($tmp_name, BinaryStream::modeWrite);
- $font_obj->encode(array("OS/2"));
- $font_obj->close();
-
- // Parse the new font to get cid2gid and widths
- $font_obj = Font::load($tmp_name);
-
- // Find Unicode char map table
- $subtable = null;
- foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
- if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
- $subtable = $_subtable;
- break;
- }
- }
-
- if ($subtable) {
- $glyphIndexArray = $subtable["glyphIndexArray"];
- $hmtx = $font_obj->getData("hmtx");
-
- unset($glyphIndexArray[0xFFFF]);
-
- $cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
- $font['CIDWidths'] = array();
- foreach ($glyphIndexArray as $cid => $gid) {
- if ($cid >= 0 && $cid < 0xFFFF && $gid) {
- $cidtogid[$cid * 2] = chr($gid >> 8);
- $cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
- }
-
- $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
- $font['CIDWidths'][$cid] = $width;
- }
-
- $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
- $font['CIDtoGID_Compressed'] = true;
-
- $data = file_get_contents($tmp_name);
- } else {
- $data = file_get_contents($fbfile);
- }
-
- $font_obj->close();
- unlink($tmp_name);
- }
-
- // create the font descriptor
- $this->numObj++;
- $fontDescriptorId = $this->numObj;
-
- $this->numObj++;
- $pfbid = $this->numObj;
-
- // determine flags (more than a little flakey, hopefully will not matter much)
- $flags = 0;
-
- if ($font['ItalicAngle'] != 0) {
- $flags += pow(2, 6);
- }
-
- if ($font['IsFixedPitch'] === 'true') {
- $flags += 1;
- }
-
- $flags += pow(2, 5); // assume non-sybolic
- $list = array(
- 'Ascent' => 'Ascender',
- 'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
- 'MissingWidth' => 'MissingWidth',
- 'Descent' => 'Descender',
- 'FontBBox' => 'FontBBox',
- 'ItalicAngle' => 'ItalicAngle'
- );
- $fdopt = array(
- 'Flags' => $flags,
- 'FontName' => $adobeFontName,
- 'StemV' => $stemV
- );
-
- foreach ($list as $k => $v) {
- if (isset($font[$v])) {
- $fdopt[$k] = $font[$v];
- }
- }
-
- if (strtolower($fbtype) === 'pfb') {
- $fdopt['FontFile'] = $pfbid;
- } elseif (strtolower($fbtype) === 'ttf') {
- $fdopt['FontFile2'] = $pfbid;
- }
-
- $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
-
- // embed the font program
- $this->o_contents($this->numObj, 'new');
- $this->objects[$pfbid]['c'] .= $data;
-
- // determine the cruicial lengths within this file
- if (strtolower($fbtype) === 'pfb') {
- $l1 = strpos($data, 'eexec') + 6;
- $l2 = strpos($data, '00000000') - $l1;
- $l3 = mb_strlen($data, '8bit') - $l2 - $l1;
- $this->o_contents(
- $this->numObj,
- 'add',
- array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)
- );
- } elseif (strtolower($fbtype) == 'ttf') {
- $l1 = mb_strlen($data, '8bit');
- $this->o_contents($this->numObj, 'add', array('Length1' => $l1));
- }
-
- // tell the font object about all this new stuff
- $tmp = array(
- 'BaseFont' => $adobeFontName,
- 'MissingWidth' => $missing_width,
- 'Widths' => $widthid,
- 'FirstChar' => $firstChar,
- 'LastChar' => $lastChar,
- 'FontDescriptor' => $fontDescriptorId
- );
-
- if (strtolower($fbtype) === 'ttf') {
- $tmp['SubType'] = 'TrueType';
- }
-
- $this->addMessage("adding extra info to font.($fontObj)");
-
- foreach ($tmp as $fk => $fv) {
- $this->addMessage("$fk : $fv");
- }
-
- $this->o_font($fontObj, 'add', $tmp);
+ if (file_exists("$fontName.ttf")) {
+ $fileSuffix = 'ttf';
+ } elseif (file_exists("$fontName.TTF")) {
+ $fileSuffix = 'TTF';
+ } elseif (file_exists("$fontName.pfb")) {
+ $fileSuffix = 'pfb';
+ } elseif (file_exists("$fontName.PFB")) {
+ $fileSuffix = 'PFB';
} else {
- $this->addMessage(
- 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
- );
+ $fileSuffix = '';
}
+ $font['fileSuffix'] = $fileSuffix;
+
+ $font['fontNum'] = $this->numFonts;
+ $font['isSubsetting'] = $isSubsetting && $font['isUnicode'] && strtolower($fileSuffix) === 'ttf';
+
// also set the differences here, note that this means that these will take effect only the
//first time that a font is selected, else they are ignored
if (isset($options['differences'])) {
@@ -2990,12 +3563,9 @@ EOT;
// applied to it as well.
$this->currentFont = $this->currentBaseFont;
$this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
-
- //$this->setCurrentFont();
}
return $this->currentFontNum;
- //return $this->numObj;
}
/**
@@ -3062,7 +3632,7 @@ EOT;
*/
function setColor($color, $force = false)
{
- $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
+ $new_color = [$color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null];
if (!$force && $this->currentColor == $new_color) {
return;
@@ -3086,7 +3656,7 @@ EOT;
*/
function setFillRule($fillRule)
{
- if (!in_array($fillRule, array("nonzero", "evenodd"))) {
+ if (!in_array($fillRule, ["nonzero", "evenodd"])) {
return;
}
@@ -3101,7 +3671,7 @@ EOT;
*/
function setStrokeColor($color, $force = false)
{
- $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
+ $new_color = [$color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null];
if (!$force && $this->currentStrokeColor == $new_color) {
return;
@@ -3149,7 +3719,7 @@ EOT;
*/
function setLineTransparency($mode, $opacity)
{
- static $blend_modes = array(
+ static $blend_modes = [
"Normal",
"Multiply",
"Screen",
@@ -3162,26 +3732,30 @@ EOT;
"SoftLight",
"Difference",
"Exclusion"
- );
+ ];
if (!in_array($mode, $blend_modes)) {
$mode = "Normal";
}
- // Only create a new graphics state if required
- if ($mode === $this->currentLineTransparency["mode"] &&
- $opacity == $this->currentLineTransparency["opacity"]
- ) {
+ if (is_null($this->currentLineTransparency)) {
+ $this->currentLineTransparency = [];
+ }
+
+ if ($mode === (key_exists('mode', $this->currentLineTransparency) ?
+ $this->currentLineTransparency['mode'] : '') &&
+ $opacity === (key_exists('opacity', $this->currentLineTransparency) ?
+ $this->currentLineTransparency["opacity"] : '')) {
return;
}
$this->currentLineTransparency["mode"] = $mode;
$this->currentLineTransparency["opacity"] = $opacity;
- $options = array(
+ $options = [
"BM" => "/$mode",
"CA" => (float)$opacity
- );
+ ];
$this->setGraphicsState($options);
}
@@ -3200,7 +3774,7 @@ EOT;
*/
function setFillTransparency($mode, $opacity)
{
- static $blend_modes = array(
+ static $blend_modes = [
"Normal",
"Multiply",
"Screen",
@@ -3213,25 +3787,30 @@ EOT;
"SoftLight",
"Difference",
"Exclusion"
- );
+ ];
if (!in_array($mode, $blend_modes)) {
$mode = "Normal";
}
- if ($mode === $this->currentFillTransparency["mode"] &&
- $opacity == $this->currentFillTransparency["opacity"]
- ) {
+ if (is_null($this->currentFillTransparency)) {
+ $this->currentFillTransparency = [];
+ }
+
+ if ($mode === (key_exists('mode', $this->currentFillTransparency) ?
+ $this->currentFillTransparency['mode'] : '') &&
+ $opacity === (key_exists('opacity', $this->currentFillTransparency) ?
+ $this->currentFillTransparency["opacity"] : '')) {
return;
}
$this->currentFillTransparency["mode"] = $mode;
$this->currentFillTransparency["opacity"] = $opacity;
- $options = array(
+ $options = [
"BM" => "/$mode",
"ca" => (float)$opacity,
- );
+ ];
$this->setGraphicsState($options);
}
@@ -3344,7 +3923,8 @@ EOT;
/**
* draw a bezier curve based on 4 control points
- */ function quadTo($cpx, $cpy, $x, $y)
+ */
+ function quadTo($cpx, $cpy, $x, $y)
{
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y));
}
@@ -3510,13 +4090,13 @@ EOT;
$string .= "$width w";
}
- $ca = array('butt' => 0, 'round' => 1, 'square' => 2);
+ $ca = ['butt' => 0, 'round' => 1, 'square' => 2];
if (isset($ca[$cap])) {
$string .= " $ca[$cap] J";
}
- $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
+ $ja = ['miter' => 0, 'round' => 1, 'bevel' => 2];
if (isset($ja[$join])) {
$string .= " $ja[$join] j";
@@ -3594,9 +4174,9 @@ EOT;
$this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height));
}
- function stroke()
+ function stroke(bool $close = false)
{
- $this->addContent("\nS");
+ $this->addContent("\n" . ($close ? "s" : "S"));
}
function fill()
@@ -3604,9 +4184,190 @@ EOT;
$this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : ""));
}
- function fillStroke()
+ function fillStroke(bool $close = false)
{
- $this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : ""));
+ $this->addContent("\n" . ($close ? "b" : "B") . ($this->fillRule === "evenodd" ? "*" : ""));
+ }
+
+ /**
+ * @param string $subtype
+ * @param integer $x
+ * @param integer $y
+ * @param integer $w
+ * @param integer $h
+ * @return int
+ */
+ function addXObject($subtype, $x, $y, $w, $h)
+ {
+ $id = ++$this->numObj;
+ $this->o_xobject($id, 'new', ['Subtype' => $subtype, 'bbox' => [$x, $y, $w, $h]]);
+ return $id;
+ }
+
+ /**
+ * @param integer $numXObject
+ * @param string $type
+ * @param array $options
+ */
+ function setXObjectResource($numXObject, $type, $options)
+ {
+ if (in_array($type, ['procset', 'font', 'xObject'])) {
+ $this->o_xobject($numXObject, $type, $options);
+ }
+ }
+
+ /**
+ * add signature
+ *
+ * $fieldSigId = $cpdf->addFormField(Cpdf::ACROFORM_FIELD_SIG, 'Signature1', 0, 0, 0, 0, 0);
+ *
+ * $signatureId = $cpdf->addSignature([
+ * 'signcert' => file_get_contents('dompdf.crt'),
+ * 'privkey' => file_get_contents('dompdf.key'),
+ * 'password' => 'password',
+ * 'name' => 'DomPDF DEMO',
+ * 'location' => 'Home',
+ * 'reason' => 'First Form',
+ * 'contactinfo' => 'info'
+ * ]);
+ * $cpdf->setFormFieldValue($fieldSigId, "$signatureId 0 R");
+ *
+ * @param string $signcert
+ * @param string $privkey
+ * @param string $password
+ * @param string|null $name
+ * @param string|null $location
+ * @param string|null $reason
+ * @param string|null $contactinfo
+ * @return int
+ */
+ function addSignature($signcert, $privkey, $password = '', $name = null, $location = null, $reason = null, $contactinfo = null) {
+ $sigId = ++$this->numObj;
+ $this->o_sig($sigId, 'new', [
+ 'SignCert' => $signcert,
+ 'PrivKey' => $privkey,
+ 'Password' => $password,
+ 'Name' => $name,
+ 'Location' => $location,
+ 'Reason' => $reason,
+ 'ContactInfo' => $contactinfo
+ ]);
+
+ return $sigId;
+ }
+
+ /**
+ * add field to form
+ *
+ * @param string $type ACROFORM_FIELD_*
+ * @param string $name
+ * @param $x0
+ * @param $y0
+ * @param $x1
+ * @param $y1
+ * @param integer $ff Field Flag ACROFORM_FIELD_*_*
+ * @param float $size
+ * @param array $color
+ * @return int
+ */
+ public function addFormField($type, $name, $x0, $y0, $x1, $y1, $ff = 0, $size = 10.0, $color = [0, 0, 0])
+ {
+ if (!$this->numFonts) {
+ $this->selectFont($this->defaultFont);
+ }
+
+ $color = implode(' ', $color) . ' rg';
+
+ $currentFontNum = $this->currentFontNum;
+ $font = array_filter($this->objects[$this->currentNode]['info']['fonts'],
+ function($item) use ($currentFontNum) { return $item['fontNum'] == $currentFontNum; });
+
+ $this->o_acroform($this->acroFormId, 'font',
+ ['objNum' => $font[0]['objNum'], 'fontNum' => $font[0]['fontNum']]);
+
+ $fieldId = ++$this->numObj;
+ $this->o_field($fieldId, 'new', [
+ 'rect' => [$x0, $y0, $x1, $y1],
+ 'F' => 4,
+ 'FT' => "/$type",
+ 'T' => $name,
+ 'Ff' => $ff,
+ 'pageid' => $this->currentPage,
+ 'da' => "$color /F$this->currentFontNum " . sprintf('%.1F Tf ', $size)
+ ]);
+
+ return $fieldId;
+ }
+
+ /**
+ * set Field value
+ *
+ * @param integer $numFieldObj
+ * @param string $value
+ */
+ public function setFormFieldValue($numFieldObj, $value)
+ {
+ $this->o_field($numFieldObj, 'set', ['value' => $value]);
+ }
+
+ /**
+ * set Field value (reference)
+ *
+ * @param integer $numFieldObj
+ * @param integer $numObj Object number
+ */
+ public function setFormFieldRefValue($numFieldObj, $numObj)
+ {
+ $this->o_field($numFieldObj, 'set', ['refvalue' => $numObj]);
+ }
+
+ /**
+ * set Field Appearanc (reference)
+ *
+ * @param integer $numFieldObj
+ * @param integer $normalNumObj
+ * @param integer|null $rolloverNumObj
+ * @param integer|null $downNumObj
+ */
+ public function setFormFieldAppearance($numFieldObj, $normalNumObj, $rolloverNumObj = null, $downNumObj = null)
+ {
+ $appearance['N'] = $normalNumObj;
+
+ if ($rolloverNumObj !== null) {
+ $appearance['R'] = $rolloverNumObj;
+ }
+
+ if ($downNumObj !== null) {
+ $appearance['D'] = $downNumObj;
+ }
+
+ $this->o_field($numFieldObj, 'set', ['appearance' => $appearance]);
+ }
+
+ /**
+ * set Choice Field option values
+ *
+ * @param integer $numFieldObj
+ * @param array $value
+ */
+ public function setFormFieldOpt($numFieldObj, $value)
+ {
+ $this->o_field($numFieldObj, 'set', ['options' => $value]);
+ }
+
+ /**
+ * add form to document
+ *
+ * @param integer $sigFlags
+ * @param boolean $needAppearances
+ */
+ public function addForm($sigFlags = 0, $needAppearances = false)
+ {
+ $this->acroFormId = ++$this->numObj;
+ $this->o_acroform($this->acroFormId, 'new', [
+ 'NeedAppearances' => $needAppearances ? 'true' : 'false',
+ 'SigFlags' => $sigFlags
+ ]);
}
/**
@@ -3715,14 +4476,14 @@ EOT;
{
$y = $this->currentPageSize["height"] - $y;
- $tm = array(
+ $tm = [
$s_x,
0,
0,
$s_y,
$x * (1 - $s_x),
$y * (1 - $s_y)
- );
+ ];
$this->transform($tm);
}
@@ -3735,14 +4496,14 @@ EOT;
*/
function translate($t_x, $t_y)
{
- $tm = array(
+ $tm = [
1,
0,
0,
1,
$t_x,
-$t_y
- );
+ ];
$this->transform($tm);
}
@@ -3762,14 +4523,14 @@ EOT;
$cos_a = cos($a);
$sin_a = sin($a);
- $tm = array(
+ $tm = [
$cos_a,
-$sin_a,
$sin_a,
$cos_a,
$x - $sin_a * $y - $cos_a * $x,
$y - $cos_a * $y + $sin_a * $x,
- );
+ ];
$this->transform($tm);
}
@@ -3789,14 +4550,14 @@ EOT;
$tan_x = tan(deg2rad($angle_x));
$tan_y = tan(deg2rad($angle_y));
- $tm = array(
+ $tm = [
1,
-$tan_y,
-$tan_x,
1,
$tan_x * $y,
$tan_y * $x,
- );
+ ];
$this->transform($tm);
}
@@ -3837,7 +4598,7 @@ EOT;
// the id from the ezPdf class is the id of the contents of the page, not the page object itself
// query that object to find the parent
$rid = $this->objects[$id]['onPage'];
- $opt = array('rid' => $rid, 'pos' => $pos);
+ $opt = ['rid' => $rid, 'pos' => $pos];
$this->o_page($this->numObj, 'new', $opt);
} else {
$this->o_page($this->numObj, 'new');
@@ -3874,7 +4635,7 @@ EOT;
* @param string $filename The filename to present to the client.
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
*/
- function stream($filename = "document.pdf", $options = array())
+ function stream($filename = "document.pdf", $options = [])
{
if (headers_sent()) {
die("Unable to stream pdf: headers already sent");
@@ -3890,7 +4651,7 @@ EOT;
header("Content-Type: application/pdf");
header("Content-Length: " . mb_strlen($tmp, "8bit"));
- $filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
+ $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf";
$attachment = $options["Attachment"] ? "attachment" : "inline";
$encoding = mb_detect_encoding($filename);
@@ -4018,7 +4779,7 @@ EOT;
}
// the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
- return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
+ return strtr($text, [')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r']);
}
/**
@@ -4039,8 +4800,8 @@ EOT;
function utf8toCodePointsArray(&$text)
{
$length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
- $unicode = array(); // array containing unicode values
- $bytes = array(); // array containing single character byte sequences
+ $unicode = []; // array containing unicode values
+ $bytes = []; // array containing single character byte sequences
$numbytes = 1; // number of octets needed to represent the UTF-8 character
for ($i = 0; $i < $length; $i++) {
@@ -4061,7 +4822,7 @@ EOT;
} else {
// use replacement character for other invalid sequences
$unicode[] = 0xFFFD;
- $bytes = array();
+ $bytes = [];
$numbytes = 1;
}
} elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
@@ -4072,7 +4833,7 @@ EOT;
for ($j = 1; $j < $numbytes; $j++) {
$c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
}
- if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
+ if ((($c >= 0xD800) and ($c <= 0xDFFF)) or ($c >= 0x10FFFF)) {
// The definition of UTF-8 prohibits encoding character numbers between
// U+D800 and U+DFFF, which are reserved for use with the UTF-16
// encoding form (as surrogate pairs) and do not directly represent
@@ -4082,13 +4843,13 @@ EOT;
$unicode[] = $c; // add char to array
}
// reset data for next char
- $bytes = array();
+ $bytes = [];
$numbytes = 1;
}
} else {
// use replacement character for other invalid sequences
$unicode[] = 0xFFFD;
- $bytes = array();
+ $bytes = [];
$numbytes = 1;
}
}
@@ -4156,7 +4917,7 @@ EOT;
$w += $wa * $nspaces;
$a = deg2rad((float)$angle);
- return array(cos($a) * $w + $x, -sin($a) * $w + $y);
+ return [cos($a) * $w + $x, -sin($a) * $w + $y];
}
/**
@@ -4194,7 +4955,7 @@ EOT;
}
if (!isset($this->stringSubsets[$font])) {
- $this->stringSubsets[$font] = array();
+ $this->stringSubsets[$font] = [];
}
$this->stringSubsets[$font] = array_unique(
@@ -4220,25 +4981,25 @@ EOT;
$this->selectFont($this->defaultFont);
}
- $text = str_replace(array("\r", "\n"), "", $text);
+ $text = str_replace(["\r", "\n"], "", $text);
- if ($smallCaps) {
- preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
- $lower = $this->concatMatches($matches);
- d($lower);
+ // if ($smallCaps) {
+ // preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
+ // $lower = $this->concatMatches($matches);
+ // d($lower);
- preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
- $other = $this->concatMatches($matches);
- d($other);
+ // preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
+ // $other = $this->concatMatches($matches);
+ // d($other);
- //$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
- }
+ // $text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
+ // }
// if there are any open callbacks, then they should be called, to show the start of the line
if ($this->nCallback > 0) {
for ($i = $this->nCallback; $i > 0; $i--) {
// call each function
- $info = array(
+ $info = [
'x' => $x,
'y' => $y,
'angle' => $angle,
@@ -4247,7 +5008,7 @@ EOT;
'nCallback' => $this->callback[$i]['nCallback'],
'height' => $this->callback[$i]['height'],
'descender' => $this->callback[$i]['descender']
- );
+ ];
$func = $this->callback[$i]['f'];
$this->$func($info);
@@ -4278,8 +5039,7 @@ EOT;
$part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
$place_text = $this->filterText($part, false);
// modify unicode text so that extra word spacing is manually implemented (bug #)
- $cf = $this->currentFont;
- if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
+ if ($this->fonts[$this->currentFont]['isUnicode'] && $wordSpaceAdjust != 0) {
$space_scale = 1000 / $size;
$place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text);
}
@@ -4302,7 +5062,7 @@ EOT;
for ($i = $this->nCallback; $i > 0; $i--) {
// call each function
$tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
- $info = array(
+ $info = [
'x' => $tmp[0],
'y' => $tmp[1],
'angle' => $angle,
@@ -4311,26 +5071,30 @@ EOT;
'nCallback' => $this->callback[$i]['nCallback'],
'height' => $this->callback[$i]['height'],
'descender' => $this->callback[$i]['descender']
- );
+ ];
$func = $this->callback[$i]['f'];
$this->$func($info);
}
}
+
+ if ($this->fonts[$this->currentFont]['isSubsetting']) {
+ $this->registerText($this->currentFont, $text);
+ }
}
/**
* calculate how wide a given text string will be on a page, at a given size.
* this can be called externally, but is also used by the other class functions
*
- * @param $size
- * @param $text
- * @param int $word_spacing
- * @param int $char_spacing
- * @return float|int
+ * @param float $size
+ * @param string $text
+ * @param float $word_spacing
+ * @param float $char_spacing
+ * @return float
*/
function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0)
{
- static $ord_cache = array();
+ static $ord_cache = [];
// this function should not change any of the settings, though it will need to
// track any directives which change during calculation, so copy them at the start
@@ -4341,7 +5105,7 @@ EOT;
$this->selectFont($this->defaultFont);
}
- $text = str_replace(array("\r", "\n"), "", $text);
+ $text = str_replace(["\r", "\n"], "", $text);
// converts a number or a float to a string so it can get the width
$text = "$text";
@@ -4352,7 +5116,6 @@ EOT;
$cf = $this->currentFont;
$current_font = $this->fonts[$cf];
$space_scale = 1000 / ($size > 0 ? $size : 1);
- $n_spaces = 0;
if ($current_font['isUnicode']) {
// for Unicode, use the code points array to calculate width rather
@@ -4374,14 +5137,13 @@ EOT;
// add additional padding for space
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
$w += $word_spacing * $space_scale;
- $n_spaces++;
}
}
}
// add additional char spacing
if ($char_spacing != 0) {
- $w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
+ $w += $char_spacing * $space_scale * count($unicode);
}
} else {
@@ -4410,14 +5172,13 @@ EOT;
// add additional padding for space
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
$w += $word_spacing * $space_scale;
- $n_spaces++;
}
}
}
// add additional char spacing
if ($char_spacing != 0) {
- $w += $char_spacing * $space_scale * ($len + $n_spaces);
+ $w += $char_spacing * $space_scale * $len;
}
}
@@ -4448,11 +5209,11 @@ EOT;
// $this->currentLineStyle = $opt['lin'];
} else {
$this->nStateStack++;
- $this->stateStack[$this->nStateStack] = array(
+ $this->stateStack[$this->nStateStack] = [
'col' => $this->currentColor,
'str' => $this->currentStrokeColor,
'lin' => $this->currentLineStyle
- );
+ ];
}
$this->save();
@@ -4490,7 +5251,7 @@ EOT;
function openObject()
{
$this->nStack++;
- $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
+ $this->stack[$this->nStack] = ['c' => $this->currentContents, 'p' => $this->currentPage];
// add a new object of the content type, to hold the data flow
$this->numObj++;
$this->o_contents($this->numObj, 'new');
@@ -4508,7 +5269,7 @@ EOT;
function reopenObject($id)
{
$this->nStack++;
- $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
+ $this->stack[$this->nStack] = ['c' => $this->currentContents, 'p' => $this->currentPage];
$this->currentContents = $id;
// also if this object is the primary contents for a page, then set the current page to its parent
@@ -4621,7 +5382,7 @@ EOT;
}
/**
- * restore an object from its stored representation. returns its new object id.
+ * restore an object from its stored representation. Returns its new object id.
*
* @param $obj
* @return int
@@ -4635,6 +5396,27 @@ EOT;
return $obj_id;
}
+ /**
+ * Embeds a file inside the PDF
+ *
+ * @param string $filepath path to the file to store inside the PDF
+ * @param string $embeddedFilename the filename displayed in the list of embedded files
+ * @param string $description a description in the list of embedded files
+ */
+ public function addEmbeddedFile(string $filepath, string $embeddedFilename, string $description): void
+ {
+ $this->numObj++;
+ $this->o_embedded_file_dictionary(
+ $this->numObj,
+ 'new',
+ [
+ 'filepath' => $filepath,
+ 'filename' => $embeddedFilename,
+ 'description' => $description
+ ]
+ );
+ }
+
/**
* add content to the documents info object
*
@@ -4667,10 +5449,10 @@ EOT;
// this will only work if the label is one of the valid ones.
if (is_array($label)) {
foreach ($label as $l => $v) {
- $this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
+ $this->o_catalog($this->catalogId, 'viewerPreferences', [$l => $v]);
}
} else {
- $this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
+ $this->o_catalog($this->catalogId, 'viewerPreferences', [$label => $value]);
}
}
@@ -4698,7 +5480,7 @@ EOT;
* Check if image already added to pdf image directory.
* If yes, need not to create again (pass empty data)
*
- * @param $imgname
+ * @param string $imgname
* @return bool
*/
function image_iscached($imgname)
@@ -4710,17 +5492,17 @@ EOT;
* add a PNG image into the document, from a GD object
* this should work with remote files
*
+ * @param \GdImage|resource $img A GD resource
* @param string $file The PNG file
* @param float $x X position
* @param float $y Y position
* @param float $w Width
* @param float $h Height
- * @param resource $img A GD resource
* @param bool $is_mask true if the image is a mask
* @param bool $mask true if the image is masked
* @throws Exception
*/
- function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null)
+ function addImagePng(&$img, $file, $x, $y, $w = 0.0, $h = 0.0, $is_mask = false, $mask = null)
{
if (!function_exists("imagepng")) {
throw new \Exception("The PHP GD extension is required, but is not installed.");
@@ -4772,7 +5554,7 @@ EOT;
}
} //End isset($this->imagelist[$file]) (png Duplicate removal)
- $this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
+ $this->addPngFromBuf($data, $file, $x, $y, $w, $h, $is_mask, $mask);
}
/**
@@ -4856,7 +5638,15 @@ EOT;
// the first version containing it was 3.0.1RC1
static $imagickClonable = null;
if ($imagickClonable === null) {
- $imagickClonable = version_compare(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0;
+ $imagickClonable = true;
+ if (defined('Imagick::IMAGICK_EXTVER')) {
+ $imagickVersion = \Imagick::IMAGICK_EXTVER;
+ } else {
+ $imagickVersion = '0';
+ }
+ if (version_compare($imagickVersion, '0.0.1', '>=')) {
+ $imagickClonable = version_compare($imagickVersion, '3.0.1rc1', '>=');
+ }
}
$imagick = new \Imagick($file);
@@ -4866,7 +5656,10 @@ EOT;
if ($imagick->getImageAlphaChannel() !== 0) {
$alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
$alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA);
- $alpha_channel->negateImage(true);
+ // Since ImageMagick7 negate invert transparency as default
+ if (\Imagick::getVersion()['versionNumber'] < 1800) {
+ $alpha_channel->negateImage(true);
+ }
$alpha_channel->writeImage($tempfile_alpha);
// Cast to 8bit+palette
@@ -4889,7 +5682,7 @@ EOT;
$imgplain = imagecreatefrompng($tempfile_plain);
} else {
// allocated colors cache
- $allocated_colors = array();
+ $allocated_colors = [];
// extract alpha channel
for ($xpx = 0; $xpx < $wpx; ++$xpx) {
@@ -4901,7 +5694,7 @@ EOT;
if ($eight_bit) {
// with gamma correction
$gammacorr = 2.2;
- $pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
+ $pixel = round(pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255);
} else {
// without gamma correction
$pixel = (127 - $alpha) * 2;
@@ -4931,21 +5724,19 @@ EOT;
imagepng($imgplain, $tempfile_plain);
}
+ $this->imageAlphaList[$file] = [$tempfile_alpha, $tempfile_plain];
+
// embed mask image
if ($tempfile_alpha) {
- $this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
+ $this->addImagePng($imgalpha, $tempfile_alpha, $x, $y, $w, $h, true);
imagedestroy($imgalpha);
+ $this->imageCache[] = $tempfile_alpha;
}
// embed image, masked with previously embedded mask
- $this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, ($tempfile_alpha !== null));
+ $this->addImagePng($imgplain, $tempfile_plain, $x, $y, $w, $h, false, ($tempfile_alpha !== null));
imagedestroy($imgplain);
-
- // remove temp files
- if ($tempfile_alpha) {
- unlink($tempfile_alpha);
- }
- unlink($tempfile_plain);
+ $this->imageCache[] = $tempfile_plain;
}
/**
@@ -4965,6 +5756,19 @@ EOT;
throw new \Exception("The PHP GD extension is required, but is not installed.");
}
+ if (isset($this->imageAlphaList[$file])) {
+ [$alphaFile, $plainFile] = $this->imageAlphaList[$file];
+
+ if ($alphaFile) {
+ $img = null;
+ $this->addImagePng($img, $alphaFile, $x, $y, $w, $h, true);
+ }
+
+ $img = null;
+ $this->addImagePng($img, $plainFile, $x, $y, $w, $h, false, ($plainFile !== null));
+ return;
+ }
+
//if already cached, need not to read again
if (isset($this->imagelist[$file])) {
$img = null;
@@ -4978,7 +5782,7 @@ EOT;
// 3 => indexed
// 4 => greyscale with alpha
// 6 => fullcolor with alpha
- $is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
+ $is_alpha = in_array($color_type, [4, 6]) || ($color_type == 3 && $bit_depth != 4);
if ($is_alpha) { // exclude grayscale alpha
$this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
@@ -5018,52 +5822,26 @@ EOT;
imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy);
imagedestroy($imgtmp);
}
- $this->addImagePng($file, $x, $y, $w, $h, $img);
+ $this->addImagePng($img, $file, $x, $y, $w, $h);
if ($img) {
imagedestroy($img);
}
}
- /**
- * add a PNG image into the document, from a file
- * this should work with remote files
- *
- * @param $file
- * @param $x
- * @param $y
- * @param int $w
- * @param int $h
- */
- function addSvgFromFile($file, $x, $y, $w = 0, $h = 0)
- {
- $doc = new \Svg\Document();
- $doc->loadFile($file);
- $dimensions = $doc->getDimensions();
-
- $this->save();
-
- $this->transform(array($w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y));
-
- $surface = new \Svg\Surface\SurfaceCpdf($doc, $this);
- $doc->render($surface);
-
- $this->restore();
- }
-
/**
* add a PNG image into the document, from a memory buffer of the file
*
+ * @param $data
* @param $file
* @param $x
* @param $y
* @param float $w
* @param float $h
- * @param $data
* @param bool $is_mask
* @param null $mask
*/
- function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null)
+ function addPngFromBuf(&$data, $file, $x, $y, $w = 0.0, $h = 0.0, $is_mask = false, $mask = null)
{
if (isset($this->imagelist[$file])) {
$data = null;
@@ -5100,7 +5878,7 @@ EOT;
// cycle through the file, identifying chunks
$haveHeader = 0;
- $info = array();
+ $info = [];
$idata = '';
$pdata = '';
@@ -5155,7 +5933,7 @@ EOT;
case 'tRNS':
//this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
//print "tRNS found, color type = ".$info['colorType']."\n";
- $transparency = array();
+ $transparency = [];
switch ($info['colorType']) {
// indexed color, rbg
@@ -5301,7 +6079,7 @@ EOT;
$this->numObj++;
// $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
- $options = array(
+ $options = [
'label' => $label,
'data' => $idata,
'bitsPerComponent' => $info['bitDepth'],
@@ -5313,14 +6091,14 @@ EOT;
'ncolor' => $ncolor,
'masked' => $mask,
'isMask' => $is_mask
- );
+ ];
if (isset($transparency)) {
$options['transparency'] = $transparency;
}
$this->o_image($this->numObj, 'new', $options);
- $this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']);
+ $this->imagelist[$file] = ['label' => $label, 'w' => $info['width'], 'h' => $info['height']];
}
if ($is_mask) {
@@ -5392,31 +6170,31 @@ EOT;
$h = $w * $imageHeight / $imageWidth;
}
- $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
+ $this->addJpegImage_common($data, $img, $imageWidth, $imageHeight, $x, $y, $w, $h, $channels);
}
/**
* common code used by the two JPEG adding functions
* @param $data
+ * @param $imgname
+ * @param $imageWidth
+ * @param $imageHeight
* @param $x
* @param $y
* @param int $w
* @param int $h
- * @param $imageWidth
- * @param $imageHeight
* @param int $channels
- * @param $imgname
*/
private function addJpegImage_common(
&$data,
+ $imgname,
+ $imageWidth,
+ $imageHeight,
$x,
$y,
$w = 0,
$h = 0,
- $imageWidth,
- $imageHeight,
- $channels = 3,
- $imgname
+ $channels = 3
) {
if ($this->image_iscached($imgname)) {
$label = $this->imagelist[$imgname]['label'];
@@ -5440,21 +6218,21 @@ EOT;
$this->o_image(
$this->numObj,
'new',
- array(
+ [
'label' => $label,
'data' => &$data,
'iw' => $imageWidth,
'ih' => $imageHeight,
'channels' => $channels
- )
+ ]
);
- $this->imagelist[$imgname] = array(
+ $this->imagelist[$imgname] = [
'label' => $label,
'w' => $imageWidth,
'h' => $imageHeight,
'c' => $channels
- );
+ ];
}
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
@@ -5484,7 +6262,7 @@ EOT;
$this->o_destination(
$this->numObj,
'new',
- array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
+ ['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c]
);
$id = $this->catalogId;
$this->o_catalog($id, 'openHere', $this->numObj);
@@ -5518,7 +6296,7 @@ EOT;
$this->o_destination(
$this->numObj,
'new',
- array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
+ ['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c]
);
$id = $this->numObj;
@@ -5542,28 +6320,28 @@ EOT;
// these font families will be used to enable bold and italic markers to be included
// within text streams. html forms will be used...
$this->fontFamilies['Helvetica.afm'] =
- array(
+ [
'b' => 'Helvetica-Bold.afm',
'i' => 'Helvetica-Oblique.afm',
'bi' => 'Helvetica-BoldOblique.afm',
'ib' => 'Helvetica-BoldOblique.afm'
- );
+ ];
$this->fontFamilies['Courier.afm'] =
- array(
+ [
'b' => 'Courier-Bold.afm',
'i' => 'Courier-Oblique.afm',
'bi' => 'Courier-BoldOblique.afm',
'ib' => 'Courier-BoldOblique.afm'
- );
+ ];
$this->fontFamilies['Times-Roman.afm'] =
- array(
+ [
'b' => 'Times-Bold.afm',
'i' => 'Times-Italic.afm',
'bi' => 'Times-BoldItalic.afm',
'ib' => 'Times-BoldItalic.afm'
- );
+ ];
}
} else {
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php
similarity index 87%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php
index fc8579735..62cc74a1d 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien M�nager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -15,7 +15,7 @@ class SurfaceCpdf implements SurfaceInterface
{
const DEBUG = false;
- /** @var \CPdf\CPdf */
+ /** @var \Svg\Surface\CPdf */
private $canvas;
private $width;
@@ -33,7 +33,7 @@ class SurfaceCpdf implements SurfaceInterface
$h = $dimensions["height"];
if (!$canvas) {
- $canvas = new \CPdf\CPdf(array(0, 0, $w, $h));
+ $canvas = new \Svg\Surface\CPdf(array(0, 0, $w, $h));
$refl = new \ReflectionClass($canvas);
$canvas->fontcache = realpath(dirname($refl->getFileName()) . "/../../fonts/")."/";
}
@@ -122,10 +122,10 @@ class SurfaceCpdf implements SurfaceInterface
$this->canvas->closePath();
}
- public function fillStroke()
+ public function fillStroke(bool $close = false)
{
if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->fillStroke();
+ $this->canvas->fillStroke($close);
}
public function clip()
@@ -173,7 +173,7 @@ class SurfaceCpdf implements SurfaceInterface
$data = file_get_contents($image);
}
- $image = tempnam("", "svg");
+ $image = tempnam(sys_get_temp_dir(), "svg");
file_put_contents($image, $data);
$img = $this->image($image, $sx, $sy, $sw, $sh, "normal");
@@ -342,10 +342,10 @@ class SurfaceCpdf implements SurfaceInterface
$this->stroke();
}
- public function stroke()
+ public function stroke(bool $close = false)
{
if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->stroke();
+ $this->canvas->stroke($close);
}
public function endPath()
@@ -415,11 +415,19 @@ class SurfaceCpdf implements SurfaceInterface
$dashArray = preg_split('/\s*,\s*/', $style->strokeDasharray);
}
+
+ $phase=0;
+ if ($style->strokeDashoffset) {
+ $phase = $style->strokeDashoffset;
+ }
+
+
$canvas->setLineStyle(
$style->strokeWidth,
$style->strokeLinecap,
$style->strokeLinejoin,
- $dashArray
+ $dashArray,
+ $phase
);
$this->setFont($style->fontFamily, $style->fontStyle, $style->fontWeight);
@@ -427,51 +435,52 @@ class SurfaceCpdf implements SurfaceInterface
public function setFont($family, $style, $weight)
{
- $map = array(
- "serif" => "Times",
- "sans-serif" => "Helvetica",
- "fantasy" => "Symbol",
- "cursive" => "Times",
- "monospace" => "Courier",
+ $map = [
+ "serif" => "times",
+ "sans-serif" => "helvetica",
+ "fantasy" => "symbol",
+ "cursive" => "times",
+ "monospace" => "courier"
+ ];
- "arial" => "Helvetica",
- "verdana" => "Helvetica",
- );
+ $styleMap = [
+ "courier" => [
+ "" => "Courier",
+ "b" => "Courier-Bold",
+ "i" => "Courier-Oblique",
+ "bi" => "Courier-BoldOblique",
+ ],
+ "helvetica" => [
+ "" => "Helvetica",
+ "b" => "Helvetica-Bold",
+ "i" => "Helvetica-Oblique",
+ "bi" => "Helvetica-BoldOblique",
+ ],
+ "symbol" => [
+ "" => "Symbol"
+ ],
+ "times" => [
+ "" => "Times-Roman",
+ "b" => "Times-Bold",
+ "i" => "Times-Italic",
+ "bi" => "Times-BoldItalic",
+ ],
+ ];
- $styleMap = array(
- 'Helvetica' => array(
- 'b' => 'Helvetica-Bold',
- 'i' => 'Helvetica-Oblique',
- 'bi' => 'Helvetica-BoldOblique',
- ),
- 'Courier' => array(
- 'b' => 'Courier-Bold',
- 'i' => 'Courier-Oblique',
- 'bi' => 'Courier-BoldOblique',
- ),
- 'Times' => array(
- '' => 'Times-Roman',
- 'b' => 'Times-Bold',
- 'i' => 'Times-Italic',
- 'bi' => 'Times-BoldItalic',
- ),
- );
-
- $family = strtolower($family);
- $style = strtolower($style);
- $weight = strtolower($weight);
-
- if (isset($map[$family])) {
- $family = $map[$family];
+ $family_lc = strtolower($family);
+ if (isset($map[$family_lc])) {
+ $family = $map[$family_lc];
}
if (isset($styleMap[$family])) {
$key = "";
+ $weight = strtolower($weight);
if ($weight === "bold" || $weight === "bolder" || (is_numeric($weight) && $weight >= 600)) {
$key .= "b";
}
+ $style = strtolower($style);
if ($style === "italic" || $style === "oblique") {
$key .= "i";
}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceInterface.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceInterface.php
similarity index 92%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceInterface.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceInterface.php
index fb007edee..25b30014d 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceInterface.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceInterface.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien M�nager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -37,11 +37,11 @@ interface SurfaceInterface
public function fill();
- public function stroke();
+ public function stroke(bool $close = false);
public function endPath();
- public function fillStroke();
+ public function fillStroke(bool $close = false);
public function clip();
@@ -87,4 +87,4 @@ interface SurfaceInterface
public function getStyle();
public function setFont($family, $style, $weight);
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php
similarity index 94%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php
index a4d17342b..3d25aef81 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien M�nager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -120,10 +120,14 @@ class SurfacePDFLib implements SurfaceInterface
$this->canvas->closepath();
}
- public function fillStroke()
+ public function fillStroke(bool $close = false)
{
if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->fill_stroke();
+ if ($close) {
+ $this->canvas->closepath_fill_stroke();
+ } else {
+ $this->canvas->fill_stroke();
+ }
}
public function clip()
@@ -159,7 +163,7 @@ class SurfacePDFLib implements SurfaceInterface
$data = file_get_contents($image);
}
- $image = tempnam("", "svg");
+ $image = tempnam(sys_get_temp_dir(), "svg");
file_put_contents($image, $data);
$img = $this->canvas->load_image("auto", $image, "");
@@ -281,10 +285,14 @@ class SurfacePDFLib implements SurfaceInterface
$this->stroke();
}
- public function stroke()
+ public function stroke(bool $close = false)
{
if (self::DEBUG) echo __FUNCTION__ . "\n";
- $this->canvas->stroke();
+ if ($close) {
+ $this->canvas->closepath_stroke();
+ } else {
+ $this->canvas->stroke();
+ }
}
public function endPath()
@@ -315,7 +323,7 @@ class SurfacePDFLib implements SurfaceInterface
$this->style = $style;
$canvas = $this->canvas;
- if ($stroke = $style->stroke && is_array($style->stroke)) {
+ if (is_array($style->stroke) && $stroke = $style->stroke) {
$canvas->setcolor(
"stroke",
"rgb",
@@ -326,7 +334,7 @@ class SurfacePDFLib implements SurfaceInterface
);
}
- if ($fill = $style->fill && is_array($style->fill)) {
+ if (is_array($style->fill) && $fill = $style->fill) {
$canvas->setcolor(
"fill",
"rgb",
@@ -419,4 +427,4 @@ class SurfacePDFLib implements SurfaceInterface
{
// TODO: Implement setFont() method.
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/AbstractTag.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/AbstractTag.php
similarity index 57%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/AbstractTag.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/AbstractTag.php
index 7101ade83..9fa679320 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/AbstractTag.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/AbstractTag.php
@@ -2,12 +2,13 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
namespace Svg\Tag;
+use Svg\CssLength;
use Svg\Document;
use Svg\Style;
@@ -134,21 +135,19 @@ abstract class AbstractTag
$transform = $attributes["transform"];
- $match = array();
+ $matches = array();
preg_match_all(
- '/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is',
+ '/(matrix|translate|scale|rotate|skew|skewX|skewY)\((.*?)\)/is',
$transform,
- $match,
+ $matches,
PREG_SET_ORDER
);
$transformations = array();
- if (count($match[0])) {
- foreach ($match as $_match) {
- $arguments = preg_split('/[ ,]+/', $_match[2]);
- array_unshift($arguments, $_match[1]);
- $transformations[] = $arguments;
- }
+ foreach ($matches as $match) {
+ $arguments = preg_split('/[ ,]+/', $match[2]);
+ array_unshift($arguments, $match[1]);
+ $transformations[] = $arguments;
}
foreach ($transformations as $t) {
@@ -166,18 +165,72 @@ abstract class AbstractTag
break;
case "rotate":
- $surface->rotate($t[1]);
+ if (isset($t[2])) {
+ $t[3] = isset($t[3]) ? $t[3] : 0;
+ $surface->translate($t[2], $t[3]);
+ $surface->rotate($t[1]);
+ $surface->translate(-$t[2], -$t[3]);
+ } else {
+ $surface->rotate($t[1]);
+ }
break;
case "skewX":
- $surface->skewX($t[1]);
+ $tan_x = tan(deg2rad($t[1]));
+ $surface->transform(1, 0, $tan_x, 1, 0, 0);
break;
case "skewY":
- $surface->skewY($t[1]);
+ $tan_y = tan(deg2rad($t[1]));
+ $surface->transform(1, $tan_y, 0, 1, 0, 0);
break;
}
}
}
}
+
+ /**
+ * Convert the given size for the context of this current tag.
+ * Takes a pixel-based reference, which is usually specific to the context of the size,
+ * but the actual reference size will be decided based upon the unit used.
+ *
+ * @param string $size
+ * @param float $pxReference
+ *
+ * @return float
+ */
+ protected function convertSize(string $size, float $pxReference): float
+ {
+ $length = new CssLength($size);
+ $reference = $pxReference;
+ $defaultFontSize = 12;
+
+ switch ($length->getUnit()) {
+ case "em":
+ $reference = $this->style->fontSize ?? $defaultFontSize;
+ break;
+ case "rem":
+ $reference = $this->document->style->fontSize ?? $defaultFontSize;
+ break;
+ case "ex":
+ case "ch":
+ $emRef = $this->style->fontSize ?? $defaultFontSize;
+ $reference = $emRef * 0.5;
+ break;
+ case "vw":
+ $reference = $this->getDocument()->getWidth();
+ break;
+ case "vh":
+ $reference = $this->getDocument()->getHeight();
+ break;
+ case "vmin":
+ $reference = min($this->getDocument()->getHeight(), $this->getDocument()->getWidth());
+ break;
+ case "vmax":
+ $reference = max($this->getDocument()->getHeight(), $this->getDocument()->getWidth());
+ break;
+ }
+
+ return (new CssLength($size))->toPixels($reference);
+ }
}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Anchor.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Anchor.php
similarity index 77%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Anchor.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Anchor.php
index 9a4b3fe21..6979495c1 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Anchor.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Anchor.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -11,4 +11,4 @@ namespace Svg\Tag;
class Anchor extends Group
{
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Circle.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Circle.php
similarity index 54%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Circle.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Circle.php
index 2e516b408..e504ffe3a 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Circle.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Circle.php
@@ -2,12 +2,14 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
namespace Svg\Tag;
+use Svg\Style;
+
class Circle extends Shape
{
protected $cx = 0;
@@ -17,15 +19,18 @@ class Circle extends Shape
public function start($attributes)
{
if (isset($attributes['cx'])) {
- $this->cx = $attributes['cx'];
+ $width = $this->document->getWidth();
+ $this->cx = $this->convertSize($attributes['cx'], $width);
}
if (isset($attributes['cy'])) {
- $this->cy = $attributes['cy'];
+ $height = $this->document->getHeight();
+ $this->cy = $this->convertSize($attributes['cy'], $height);
}
if (isset($attributes['r'])) {
- $this->r = $attributes['r'];
+ $diagonal = $this->document->getDiagonal();
+ $this->r = $this->convertSize($attributes['r'], $diagonal);
}
$this->document->getSurface()->circle($this->cx, $this->cy, $this->r);
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/ClipPath.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/ClipPath.php
similarity index 91%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/ClipPath.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/ClipPath.php
index 54ee084c4..46722f934 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/ClipPath.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/ClipPath.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -30,4 +30,4 @@ class ClipPath extends AbstractTag
{
$this->document->getSurface()->restore();
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Ellipse.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Ellipse.php
similarity index 60%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Ellipse.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Ellipse.php
index 483e51e74..42891e031 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Ellipse.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Ellipse.php
@@ -2,12 +2,14 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
namespace Svg\Tag;
+use Svg\Style;
+
class Ellipse extends Shape
{
protected $cx = 0;
@@ -19,19 +21,22 @@ class Ellipse extends Shape
{
parent::start($attributes);
+ $width = $this->document->getWidth();
+ $height = $this->document->getHeight();
+
if (isset($attributes['cx'])) {
- $this->cx = $attributes['cx'];
+ $this->cx = $this->convertSize($attributes['cx'], $width);
}
if (isset($attributes['cy'])) {
- $this->cy = $attributes['cy'];
+ $this->cy = $this->convertSize($attributes['cy'], $height);
}
if (isset($attributes['rx'])) {
- $this->rx = $attributes['rx'];
+ $this->rx = $this->convertSize($attributes['rx'], $width);
}
if (isset($attributes['ry'])) {
- $this->ry = $attributes['ry'];
+ $this->ry = $this->convertSize($attributes['ry'], $height);
}
$this->document->getSurface()->ellipse($this->cx, $this->cy, $this->rx, $this->ry, 0, 0, 360, false);
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Group.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Group.php
similarity index 91%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Group.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Group.php
index 542bfbdbd..bacb3851c 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Group.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Group.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -30,4 +30,4 @@ class Group extends AbstractTag
{
$this->document->getSurface()->restore();
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Image.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Image.php
similarity index 60%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Image.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Image.php
index f356b28de..bda17ea31 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Image.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Image.php
@@ -2,12 +2,14 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
namespace Svg\Tag;
+use Svg\Style;
+
class Image extends AbstractTag
{
protected $x = 0;
@@ -28,35 +30,39 @@ class Image extends AbstractTag
public function start($attributes)
{
- $document = $this->document;
$height = $this->document->getHeight();
+ $width = $this->document->getWidth();
$this->y = $height;
if (isset($attributes['x'])) {
- $this->x = $attributes['x'];
+ $this->x = $this->convertSize($attributes['x'], $width);
}
if (isset($attributes['y'])) {
- $this->y = $height - $attributes['y'];
+ $this->y = $height - $this->convertSize($attributes['y'], $height);
}
if (isset($attributes['width'])) {
- $this->width = $attributes['width'];
+ $this->width = $this->convertSize($attributes['width'], $width);
}
if (isset($attributes['height'])) {
- $this->height = $attributes['height'];
+ $this->height = $this->convertSize($attributes['height'], $height);
}
if (isset($attributes['xlink:href'])) {
$this->href = $attributes['xlink:href'];
}
- $document->getSurface()->transform(1, 0, 0, -1, 0, $height);
+ if (isset($attributes['href'])) {
+ $this->href = $attributes['href'];
+ }
- $document->getSurface()->drawImage($this->href, $this->x, $this->y, $this->width, $this->height);
+ $this->document->getSurface()->transform(1, 0, 0, -1, 0, $height);
+
+ $this->document->getSurface()->drawImage($this->href, $this->x, $this->y, $this->width, $this->height);
}
protected function after()
{
$this->document->getSurface()->restore();
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Line.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Line.php
similarity index 60%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Line.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Line.php
index 42504bca5..fb3b64c48 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Line.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Line.php
@@ -2,12 +2,14 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
namespace Svg\Tag;
+use Svg\Style;
+
class Line extends Shape
{
protected $x1 = 0;
@@ -18,21 +20,24 @@ class Line extends Shape
public function start($attributes)
{
+ $height = $this->document->getHeight();
+ $width = $this->document->getWidth();
+
if (isset($attributes['x1'])) {
- $this->x1 = $attributes['x1'];
+ $this->x1 = $this->convertSize($attributes['x1'], $width);
}
if (isset($attributes['y1'])) {
- $this->y1 = $attributes['y1'];
+ $this->y1 = $this->convertSize($attributes['y1'], $height);
}
if (isset($attributes['x2'])) {
- $this->x2 = $attributes['x2'];
+ $this->x2 = $this->convertSize($attributes['x2'], $width);
}
if (isset($attributes['y2'])) {
- $this->y2 = $attributes['y2'];
+ $this->y2 = $this->convertSize($attributes['y2'], $height);
}
$surface = $this->document->getSurface();
$surface->moveTo($this->x1, $this->y1);
$surface->lineTo($this->x2, $this->y2);
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/LinearGradient.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/LinearGradient.php
similarity index 97%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/LinearGradient.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/LinearGradient.php
index 605ec23d9..c5e63979e 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/LinearGradient.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/LinearGradient.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -80,4 +80,4 @@ class LinearGradient extends AbstractTag
return $this->stops;
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Path.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Path.php
similarity index 86%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Path.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Path.php
index c43d6388c..3dce7a619 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Path.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Path.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -12,6 +12,27 @@ use Svg\Surface\SurfaceInterface;
class Path extends Shape
{
+ // kindly borrowed from fabric.util.parsePath.
+ /* @see https://github.com/fabricjs/fabric.js/blob/master/src/util/path.js#L664 */
+ const NUMBER_PATTERN = '([-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?)\s*';
+ const COMMA_PATTERN = '(?:\s+,?\s*|,\s*)?';
+ const FLAG_PATTERN = '([01])';
+ const ARC_REGEXP = '/'
+ . self::NUMBER_PATTERN
+ . self::COMMA_PATTERN
+ . self::NUMBER_PATTERN
+ . self::COMMA_PATTERN
+ . self::NUMBER_PATTERN
+ . self::COMMA_PATTERN
+ . self::FLAG_PATTERN
+ . self::COMMA_PATTERN
+ . self::FLAG_PATTERN
+ . self::COMMA_PATTERN
+ . self::NUMBER_PATTERN
+ . self::COMMA_PATTERN
+ . self::NUMBER_PATTERN
+ . '/';
+
static $commandLengths = array(
'm' => 2,
'l' => 2,
@@ -29,24 +50,37 @@ class Path extends Shape
'M' => 'L',
);
- public function start($attributes)
+ public static function parse(string $commandSequence): array
{
- if (!isset($attributes['d'])) {
- $this->hasShape = false;
-
- return;
- }
-
$commands = array();
- preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $attributes['d'], $commands, PREG_SET_ORDER);
-
+ preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $commandSequence, $commands, PREG_SET_ORDER);
+
$path = array();
foreach ($commands as $c) {
if (count($c) == 3) {
+ $commandLower = strtolower($c[1]);
+
+ // arcs have special flags that apparently don't require spaces.
+ if ($commandLower === 'a' && preg_match_all(static::ARC_REGEXP, $c[2], $matches, PREG_PATTERN_ORDER)) {
+ $numberOfMatches = count($matches[0]);
+ for ($k = 0; $k < $numberOfMatches; ++$k) {
+ $path[] = [
+ $c[1],
+ $matches[1][$k],
+ $matches[2][$k],
+ $matches[3][$k],
+ $matches[4][$k],
+ $matches[5][$k],
+ $matches[6][$k],
+ $matches[7][$k],
+ ];
+ }
+ continue;
+ }
+
$arguments = array();
preg_match_all('/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/i', $c[2], $arguments, PREG_PATTERN_ORDER);
$item = $arguments[0];
- $commandLower = strtolower($c[1]);
if (
isset(self::$commandLengths[$commandLower]) &&
@@ -75,6 +109,18 @@ class Path extends Shape
}
}
+ return $path;
+ }
+
+ public function start($attributes)
+ {
+ if (!isset($attributes['d'])) {
+ $this->hasShape = false;
+
+ return;
+ }
+
+ $path = static::parse($attributes['d']);
$surface = $this->document->getSurface();
// From https://github.com/kangax/fabric.js/blob/master/src/shapes/path.class.js
@@ -92,7 +138,6 @@ class Path extends Shape
$tempControlY = null;
$l = 0; //-((this.width / 2) + $this.pathOffset.x),
$t = 0; //-((this.height / 2) + $this.pathOffset.y),
- $methodName = null;
foreach ($path as $current) {
switch ($current[0]) { // first letter
@@ -287,23 +332,16 @@ class Path extends Shape
$tempX = $x + $current[1];
$tempY = $y + $current[2];
- if (preg_match("/[QqTt]/", $previous[0])) {
- // If there is no previous command or if the previous command was not a Q, q, T or t,
- // assume the control point is coincident with the current point
+ // calculate reflection of previous control points
+ if (preg_match('/[QqT]/', $previous[0])) {
+ $controlX = 2 * $x - $controlX;
+ $controlY = 2 * $y - $controlY;
+ } elseif ($previous[0] === 't') {
+ $controlX = 2 * $x - $tempControlX;
+ $controlY = 2 * $y - $tempControlY;
+ } else {
$controlX = $x;
$controlY = $y;
- } else {
- if ($previous[0] === 't') {
- // calculate reflection of previous control points for t
- $controlX = 2 * $x - $tempControlX;
- $controlY = 2 * $y - $tempControlY;
- } else {
- if ($previous[0] === 'q') {
- // calculate reflection of previous control points for q
- $controlX = 2 * $x - $controlX;
- $controlY = 2 * $y - $controlY;
- }
- }
}
$tempControlX = $controlX;
@@ -317,8 +355,6 @@ class Path extends Shape
);
$x = $tempX;
$y = $tempY;
- $controlX = $x + $current[1];
- $controlY = $y + $current[2];
break;
case 'T':
@@ -326,8 +362,14 @@ class Path extends Shape
$tempY = $current[2];
// calculate reflection of previous control points
- $controlX = 2 * $x - $controlX;
- $controlY = 2 * $y - $controlY;
+ if (preg_match('/[QqTt]/', $previous[0])) {
+ $controlX = 2 * $x - $controlX;
+ $controlY = 2 * $y - $controlY;
+ } else {
+ $controlX = $x;
+ $controlY = $y;
+ }
+
$surface->quadraticCurveTo(
$controlX + $l,
$controlY + $t,
@@ -339,7 +381,6 @@ class Path extends Shape
break;
case 'a':
- // TODO: optimize this
$this->drawArc(
$surface,
$x + $l,
@@ -405,7 +446,14 @@ class Path extends Shape
array(),
);
- $segsNorm = $this->arcToSegments($tx - $fx, $ty - $fy, $rx, $ry, $large, $sweep, $rot);
+ $toX = $tx - $fx;
+ $toY = $ty - $fy;
+
+ if ($toX + $toY === 0) {
+ return;
+ }
+
+ $segsNorm = $this->arcToSegments($toX, $toY, $rx, $ry, $large, $sweep, $rot);
for ($i = 0, $len = count($segsNorm); $i < $len; $i++) {
$segs[$i][0] = $segsNorm[$i][0] + $fx;
@@ -525,4 +573,4 @@ class Path extends Shape
return 2 * M_PI - ($ta - $tb);
}
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polygon.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polygon.php
similarity index 70%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polygon.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polygon.php
index 3100c5e08..e7ca92a1b 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polygon.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polygon.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -13,16 +13,25 @@ class Polygon extends Shape
public function start($attributes)
{
$tmp = array();
- preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp);
+ preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp, PREG_PATTERN_ORDER);
$points = $tmp[0];
$count = count($points);
+ if ($count < 4) {
+ // nothing to draw
+ return;
+ }
+
$surface = $this->document->getSurface();
list($x, $y) = $points;
$surface->moveTo($x, $y);
for ($i = 2; $i < $count; $i += 2) {
+ if ($i + 1 === $count) {
+ // invalid trailing point
+ continue;
+ }
$x = $points[$i];
$y = $points[$i + 1];
$surface->lineTo($x, $y);
@@ -30,4 +39,4 @@ class Polygon extends Shape
$surface->closePath();
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polyline.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polyline.php
similarity index 69%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polyline.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polyline.php
index c2837f586..45e2131ec 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polyline.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polyline.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -13,19 +13,28 @@ class Polyline extends Shape
public function start($attributes)
{
$tmp = array();
- preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp);
+ preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp, PREG_PATTERN_ORDER);
$points = $tmp[0];
$count = count($points);
+ if ($count < 4) {
+ // nothing to draw
+ return;
+ }
+
$surface = $this->document->getSurface();
list($x, $y) = $points;
$surface->moveTo($x, $y);
for ($i = 2; $i < $count; $i += 2) {
+ if ($i + 1 === $count) {
+ // invalid trailing point
+ continue;
+ }
$x = $points[$i];
$y = $points[$i + 1];
$surface->lineTo($x, $y);
}
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/RadialGradient.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/RadialGradient.php
similarity index 82%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/RadialGradient.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/RadialGradient.php
index 93987b393..a9de62f31 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/RadialGradient.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/RadialGradient.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -14,4 +14,4 @@ class RadialGradient extends AbstractTag
{
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Rect.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Rect.php
similarity index 66%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Rect.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Rect.php
index 7d32127f6..b5f3f774b 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Rect.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Rect.php
@@ -2,12 +2,14 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
namespace Svg\Tag;
+use Svg\Style;
+
class Rect extends Shape
{
protected $x = 0;
@@ -19,18 +21,21 @@ class Rect extends Shape
public function start($attributes)
{
+ $width = $this->document->getWidth();
+ $height = $this->document->getHeight();
+
if (isset($attributes['x'])) {
- $this->x = $attributes['x'];
+ $this->x = $this->convertSize($attributes['x'], $width);
}
if (isset($attributes['y'])) {
- $this->y = $attributes['y'];
+ $this->y = $this->convertSize($attributes['y'], $height);
}
if (isset($attributes['width'])) {
- $this->width = $attributes['width'];
+ $this->width = $this->convertSize($attributes['width'], $width);
}
if (isset($attributes['height'])) {
- $this->height = $attributes['height'];
+ $this->height = $this->convertSize($attributes['height'], $height);
}
if (isset($attributes['rx'])) {
@@ -42,4 +47,4 @@ class Rect extends Shape
$this->document->getSurface()->rect($this->x, $this->y, $this->width, $this->height, $this->rx, $this->ry);
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Shape.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Shape.php
similarity index 94%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Shape.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Shape.php
index 0a2bfaef5..767e81dbf 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Shape.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Shape.php
@@ -38,7 +38,7 @@ class Shape extends AbstractTag
if ($fill) {
if ($stroke) {
- $surface->fillStroke();
+ $surface->fillStroke(false);
} else {
// if (is_string($style->fill)) {
// /** @var LinearGradient|RadialGradient $gradient */
@@ -51,7 +51,7 @@ class Shape extends AbstractTag
}
}
elseif ($stroke) {
- $surface->stroke();
+ $surface->stroke(false);
}
else {
$surface->endPath();
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Stop.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Stop.php
similarity index 81%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Stop.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Stop.php
index 666a2ac71..22c9a9888 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Stop.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Stop.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -14,4 +14,4 @@ class Stop extends AbstractTag
{
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/StyleTag.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/StyleTag.php
similarity index 89%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/StyleTag.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/StyleTag.php
index cda549346..309de01ef 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/StyleTag.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/StyleTag.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien Mnager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -24,4 +24,4 @@ class StyleTag extends AbstractTag
{
$this->text .= $text;
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Text.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Text.php
similarity index 83%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Text.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Text.php
index 83b5afe6a..80e08a600 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Text.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Text.php
@@ -8,6 +8,8 @@
namespace Svg\Tag;
+use Svg\Style;
+
class Text extends Shape
{
protected $x = 0;
@@ -16,18 +18,18 @@ class Text extends Shape
public function start($attributes)
{
- $document = $this->document;
$height = $this->document->getHeight();
$this->y = $height;
if (isset($attributes['x'])) {
- $this->x = $attributes['x'];
+ $width = $this->document->getWidth();
+ $this->x = $this->convertSize($attributes['x'], $width);
}
if (isset($attributes['y'])) {
- $this->y = $height - $attributes['y'];
+ $this->y = $height - $this->convertSize($attributes['y'], $height);
}
- $document->getSurface()->transform(1, 0, 0, -1, 0, $height);
+ $this->document->getSurface()->transform(1, 0, 0, -1, 0, $height);
}
public function end()
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/UseTag.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/UseTag.php
similarity index 75%
rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/UseTag.php
rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/UseTag.php
index b88a6cce1..c5f00ea9d 100644
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/UseTag.php
+++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/UseTag.php
@@ -2,7 +2,7 @@
/**
* @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib
- * @author Fabien M�nager
+ * @author Fabien Ménager
* @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
*/
@@ -38,7 +38,7 @@ class UseTag extends AbstractTag
$document = $this->getDocument();
- $link = $attributes["xlink:href"];
+ $link = $attributes["href"] ?? $attributes["xlink:href"];
$this->reference = $document->getDef($link);
if ($this->reference) {
@@ -69,12 +69,18 @@ class UseTag extends AbstractTag
return;
}
- $attributes = array_merge($this->reference->attributes, $attributes);
+ $mergedAttributes = $this->reference->attributes;
+ $attributesToNotMerge = ['x', 'y', 'width', 'height'];
+ foreach ($attributes as $attrKey => $attrVal) {
+ if (!in_array($attrKey, $attributesToNotMerge) && !isset($mergedAttributes[$attrKey])) {
+ $mergedAttributes[$attrKey] = $attrVal;
+ }
+ }
- $this->reference->handle($attributes);
+ $this->reference->handle($mergedAttributes);
foreach ($this->reference->children as $_child) {
- $_attributes = array_merge($_child->attributes, $attributes);
+ $_attributes = array_merge($_child->attributes, $mergedAttributes);
$_child->handle($_attributes);
}
}
@@ -93,4 +99,4 @@ class UseTag extends AbstractTag
$_child->handleEnd();
}
}
-}
\ No newline at end of file
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/CHANGELOG.md b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/CHANGELOG.md
new file mode 100644
index 000000000..a23fd0e68
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/CHANGELOG.md
@@ -0,0 +1,241 @@
+# Revision History
+
+## 8.4.0
+
+### Features
+
+* Support for PHP 8.x
+* PHPDoc annotations
+* Allow usage of CSS variables inside color functions (by parsing them as regular functions)
+* Use PSR-12 code style
+* *No deprecations*
+
+### Bugfixes
+
+* Improved handling of whitespace in `calc()`
+* Fix parsing units whose prefix is also a valid unit, like `vmin`
+* Allow passing an object to `CSSList#replace`
+* Fix PHP 7.3 warnings
+* Correctly parse keyframes with `%`
+* Don’t convert large numbers to scientific notation
+* Allow a file to end after an `@import`
+* Preserve case of CSS variables as specced
+* Allow identifiers to use escapes the same way as strings
+* No longer use `eval` for the comparison in `getSelectorsBySpecificity`, in case it gets passed untrusted input (CVE-2020-13756). Also fixed in 8.3.1, 8.2.1, 8.1.1, 8.0.1, 7.0.4, 6.0.2, 5.2.1, 5.1.3, 5.0.9, 4.0.1, 3.0.1, 2.0.1, 1.0.1.
+* Prevent an infinite loop when parsing invalid grid line names
+* Remove invalid unit `vm`
+* Retain rule order after expanding shorthands
+
+### Backwards-incompatible changes
+
+* PHP ≥ 5.6 is now required
+* HHVM compatibility target dropped
+
+## 8.3.0 (2019-02-22)
+
+* Refactor parsing logic to mostly reside in the class files whose data structure is to be parsed (this should eventually allow us to unit-test specific parts of the parsing logic individually).
+* Fix error in parsing `calc` expessions when the first operand is a negative number, thanks to @raxbg.
+* Support parsing CSS4 colors in hex notation with alpha values, thanks to @raxbg.
+* Swallow more errors in lenient mode, thanks to @raxbg.
+* Allow specifying arbitrary strings to output before and after declaration blocks, thanks to @westonruter.
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 8.2.0 (2018-07-13)
+
+* Support parsing `calc()`, thanks to @raxbg.
+* Support parsing grid-lines, again thanks to @raxbg.
+* Support parsing legacy IE filters (`progid:`) in lenient mode, thanks to @FMCorz
+* Performance improvements parsing large files, again thanks to @FMCorz
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 8.1.0 (2016-07-19)
+
+* Comments are no longer silently ignored but stored with the object with which they appear (no render support, though). Thanks to @FMCorz.
+* The IE hacks using `\0` and `\9` can now be parsed (and rendered) in lenient mode. Thanks (again) to @FMCorz.
+* Media queries with or without spaces before the query are parsed. Still no *real* parsing support, though. Sorry…
+* PHPUnit is now listed as a dev-dependency in composer.json.
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 8.0.0 (2016-06-30)
+
+* Store source CSS line numbers in tokens and parsing exceptions.
+* *No deprecations*
+
+### Backwards-incompatible changes
+
+* Unrecoverable parser errors throw an exception of type `Sabberworm\CSS\Parsing\SourceException` instead of `\Exception`.
+
+## 7.0.3 (2016-04-27)
+
+* Fixed parsing empty CSS when multibyte is off
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 7.0.2 (2016-02-11)
+
+* 150 time performance boost thanks to @[ossinkine](https://github.com/ossinkine)
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 7.0.1 (2015-12-25)
+
+* No more suppressed `E_NOTICE`
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 7.0.0 (2015-08-24)
+
+* Compatibility with PHP 7. Well timed, eh?
+* *No deprecations*
+
+### Backwards-incompatible changes
+
+* The `Sabberworm\CSS\Value\String` class has been renamed to `Sabberworm\CSS\Value\CSSString`.
+
+## 6.0.1 (2015-08-24)
+
+* Remove some declarations in interfaces incompatible with PHP 5.3 (< 5.3.9)
+* *No deprecations*
+
+## 6.0.0 (2014-07-03)
+
+* Format output using Sabberworm\CSS\OutputFormat
+* *No backwards-incompatible changes*
+
+### Deprecations
+
+* The parse() method replaces __toString with an optional argument (instance of the OutputFormat class)
+
+## 5.2.0 (2014-06-30)
+
+* Support removing a selector from a declaration block using `$oBlock->removeSelector($mSelector)`
+* Introduce a specialized exception (Sabberworm\CSS\Parsing\OuputException) for exceptions during output rendering
+
+* *No deprecations*
+
+#### Backwards-incompatible changes
+
+* Outputting a declaration block that has no selectors throws an OuputException instead of outputting an invalid ` {…}` into the CSS document.
+
+## 5.1.2 (2013-10-30)
+
+* Remove the use of consumeUntil in comment parsing. This makes it possible to parse comments such as `/** Perfectly valid **/`
+* Add fr relative size unit
+* Fix some issues with HHVM
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.1.1 (2013-10-28)
+
+* Updated CHANGELOG.md to reflect changes since 5.0.4
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.1.0 (2013-10-24)
+
+* Performance enhancements by Michael M Slusarz
+* More rescue entry points for lenient parsing (unexpected tokens between declaration blocks and unclosed comments)
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.8 (2013-08-15)
+
+* Make default settings’ multibyte parsing option dependent on whether or not the mbstring extension is actually installed.
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.7 (2013-08-04)
+
+* Fix broken decimal point output optimization
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.6 (2013-05-31)
+
+* Fix broken unit test
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.5 (2013-04-17)
+
+* Initial support for lenient parsing (setting this parser option will catch some exceptions internally and recover the parser’s state as neatly as possible).
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.4 (2013-03-21)
+
+* Don’t output floats with locale-aware separator chars
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.3 (2013-03-21)
+
+* More size units recognized
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.2 (2013-03-21)
+
+* CHANGELOG.md file added to distribution
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.1 (2013-03-20)
+
+* Internal cleanup
+* *No backwards-incompatible changes*
+* *No deprecations*
+
+## 5.0.0 (2013-03-20)
+
+* Correctly parse all known CSS 3 units (including Hz and kHz).
+* Output RGB colors in short (#aaa or #ababab) notation
+* Be case-insensitive when parsing identifiers.
+* *No deprecations*
+
+### Backwards-incompatible changes
+
+* `Sabberworm\CSS\Value\Color`’s `__toString` method overrides `CSSList`’s to maybe return something other than `type(value, …)` (see above).
+
+## 4.0.0 (2013-03-19)
+
+* Support for more @-rules
+* Generic interface `Sabberworm\CSS\Property\AtRule`, implemented by all @-rule classes
+* *No deprecations*
+
+### Backwards-incompatible changes
+
+* `Sabberworm\CSS\RuleSet\AtRule` renamed to `Sabberworm\CSS\RuleSet\AtRuleSet`
+* `Sabberworm\CSS\CSSList\MediaQuery` renamed to `Sabberworm\CSS\RuleSet\CSSList\AtRuleBlockList` with differing semantics and API (which also works for other block-list-based @-rules like `@supports`).
+
+## 3.0.0 (2013-03-06)
+
+* Support for lenient parsing (on by default)
+* *No deprecations*
+
+### Backwards-incompatible changes
+
+* All properties (like whether or not to use `mb_`-functions, which default charset to use and – new – whether or not to be forgiving when parsing) are now encapsulated in an instance of `Sabberworm\CSS\Settings` which can be passed as the second argument to `Sabberworm\CSS\Parser->__construct()`.
+* Specifying a charset as the second argument to `Sabberworm\CSS\Parser->__construct()` is no longer supported. Use `Sabberworm\CSS\Settings::create()->withDefaultCharset('some-charset')` instead.
+* Setting `Sabberworm\CSS\Parser->bUseMbFunctions` has no effect. Use `Sabberworm\CSS\Settings::create()->withMultibyteSupport(true/false)` instead.
+* `Sabberworm\CSS\Parser->parse()` may throw a `Sabberworm\CSS\Parsing\UnexpectedTokenException` when in strict parsing mode.
+
+## 2.0.0 (2013-01-29)
+
+* Allow multiple rules of the same type per rule set
+
+### Backwards-incompatible changes
+
+* `Sabberworm\CSS\RuleSet->getRules()` returns an index-based array instead of an associative array. Use `Sabberworm\CSS\RuleSet->getRulesAssoc()` (which eliminates duplicate rules and lets the later rule of the same name win).
+* `Sabberworm\CSS\RuleSet->removeRule()` works as it did before except when passed an instance of `Sabberworm\CSS\Rule\Rule`, in which case it would only remove the exact rule given instead of all the rules of the same type. To get the old behaviour, use `Sabberworm\CSS\RuleSet->removeRule($oRule->getRule()`;
+
+## 1.0
+
+Initial release of a stable public API.
+
+## 0.9
+
+Last version not to use PSR-0 project organization semantics.
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/LICENSE b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/LICENSE
new file mode 100644
index 000000000..686a4e311
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2011 Raphael Schweikert, https://www.sabberworm.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/README.md b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/README.md
new file mode 100644
index 000000000..66fb1c655
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/README.md
@@ -0,0 +1,632 @@
+# PHP CSS Parser
+
+[](https://github.com/sabberworm/PHP-CSS-Parser/actions/)
+
+A Parser for CSS Files written in PHP. Allows extraction of CSS files into a data structure, manipulation of said structure and output as (optimized) CSS.
+
+## Usage
+
+### Installation using Composer
+
+```bash
+composer require sabberworm/php-css-parser
+```
+
+### Extraction
+
+To use the CSS Parser, create a new instance. The constructor takes the following form:
+
+```php
+new \Sabberworm\CSS\Parser($css);
+```
+
+To read a file, for example, you’d do the following:
+
+```php
+$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
+$cssDocument = $parser->parse();
+```
+
+The resulting CSS document structure can be manipulated prior to being output.
+
+### Options
+
+#### Charset
+
+The charset option is used only if no `@charset` declaration is found in the CSS file. UTF-8 is the default, so you won’t have to create a settings object at all if you don’t intend to change that.
+
+```php
+$settings = \Sabberworm\CSS\Settings::create()
+ ->withDefaultCharset('windows-1252');
+$parser = new \Sabberworm\CSS\Parser($css, $settings);
+```
+
+#### Strict parsing
+
+To have the parser choke on invalid rules, supply a thusly configured `\Sabberworm\CSS\Settings` object:
+
+```php
+$parser = new \Sabberworm\CSS\Parser(
+ file_get_contents('somefile.css'),
+ \Sabberworm\CSS\Settings::create()->beStrict()
+);
+```
+
+#### Disable multibyte functions
+
+To achieve faster parsing, you can choose to have PHP-CSS-Parser use regular string functions instead of `mb_*` functions. This should work fine in most cases, even for UTF-8 files, as all the multibyte characters are in string literals. Still it’s not recommended using this with input you have no control over as it’s not thoroughly covered by test cases.
+
+```php
+$settings = \Sabberworm\CSS\Settings::create()->withMultibyteSupport(false);
+$parser = new \Sabberworm\CSS\Parser($css, $settings);
+```
+
+### Manipulation
+
+The resulting data structure consists mainly of five basic types: `CSSList`, `RuleSet`, `Rule`, `Selector` and `Value`. There are two additional types used: `Import` and `Charset`, which you won’t use often.
+
+#### CSSList
+
+`CSSList` represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc. `CSSList` has the following concrete subtypes:
+
+* `Document` – representing the root of a CSS file.
+* `MediaQuery` – represents a subsection of a `CSSList` that only applies to an output device matching the contained media query.
+
+To access the items stored in a `CSSList` – like the document you got back when calling `$parser->parse()` –, use `getContents()`, then iterate over that collection and use instanceof to check whether you’re dealing with another `CSSList`, a `RuleSet`, a `Import` or a `Charset`.
+
+To append a new item (selector, media query, etc.) to an existing `CSSList`, construct it using the constructor for this class and use the `append($oItem)` method.
+
+#### RuleSet
+
+`RuleSet` is a container for individual rules. The most common form of a rule set is one constrained by a selector. The following concrete subtypes exist:
+
+* `AtRuleSet` – for generic at-rules which do not match the ones specifically mentioned like `@import`, `@charset` or `@media`. A common example for this is `@font-face`.
+* `DeclarationBlock` – a `RuleSet` constrained by a `Selector`; contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the matching elements.
+
+Note: A `CSSList` can contain other `CSSList`s (and `Import`s as well as a `Charset`), while a `RuleSet` can only contain `Rule`s.
+
+If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` (which accepts either a `Rule` instance or a rule name; optionally suffixed by a dash to remove all related rules).
+
+#### Rule
+
+`Rule`s just have a key (the rule) and a value. These values are all instances of a `Value`.
+
+#### Value
+
+`Value` is an abstract class that only defines the `render` method. The concrete subclasses for atomic value types are:
+
+* `Size` – consists of a numeric `size` value and a unit.
+* `Color` – colors can be input in the form #rrggbb, #rgb or schema(val1, val2, …) but are always stored as an array of ('s' => val1, 'c' => val2, 'h' => val3, …) and output in the second form.
+* `CSSString` – this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes.
+* `URL` – URLs in CSS; always output in URL("") notation.
+
+There is another abstract subclass of `Value`, `ValueList`. A `ValueList` represents a lists of `Value`s, separated by some separation character (mostly `,`, whitespace, or `/`). There are two types of `ValueList`s:
+
+* `RuleValueList` – The default type, used to represent all multi-valued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;` (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list and a comma-separated list).
+* `CSSFunction` – A special kind of value that also contains a function name and where the values are the function’s arguments. Also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`.
+
+#### Convenience methods
+
+There are a few convenience methods on Document to ease finding, manipulating and deleting rules:
+
+* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested your selectors are. Aliased as `getAllSelectors()`.
+* `getAllRuleSets()` – does what it says; no matter how deeply nested your rule sets are.
+* `getAllValues()` – finds all `Value` objects inside `Rule`s.
+
+## To-Do
+
+* More convenience methods (like `selectorsWithElement($sId/Class/TagName)`, `attributesOfType($type)`, `removeAttributesOfType($type)`)
+* Real multibyte support. Currently, only multibyte charsets whose first 255 code points take up only one byte and are identical with ASCII are supported (yes, UTF-8 fits this description).
+* Named color support (using `Color` instead of an anonymous string literal)
+
+## Use cases
+
+### Use `Parser` to prepend an ID to all selectors
+
+```php
+$myId = "#my_id";
+$parser = new \Sabberworm\CSS\Parser($css);
+$cssDocument = $parser->parse();
+foreach ($cssDocument->getAllDeclarationBlocks() as $block) {
+ foreach ($block->getSelectors() as $selector) {
+ // Loop over all selector parts (the comma-separated strings in a
+ // selector) and prepend the ID.
+ $selector->setSelector($myId.' '.$selector->getSelector());
+ }
+}
+```
+
+### Shrink all absolute sizes to half
+
+```php
+$parser = new \Sabberworm\CSS\Parser($css);
+$cssDocument = $parser->parse();
+foreach ($cssDocument->getAllValues() as $value) {
+ if ($value instanceof CSSSize && !$value->isRelative()) {
+ $value->setSize($value->getSize() / 2);
+ }
+}
+```
+
+### Remove unwanted rules
+
+```php
+$parser = new \Sabberworm\CSS\Parser($css);
+$cssDocument = $parser->parse();
+foreach($cssDocument->getAllRuleSets() as $oRuleSet) {
+ // Note that the added dash will make this remove all rules starting with
+ // `font-` (like `font-size`, `font-weight`, etc.) as well as a potential
+ // `font-rule`.
+ $oRuleSet->removeRule('font-');
+ $oRuleSet->removeRule('cursor');
+}
+```
+
+### Output
+
+To output the entire CSS document into a variable, just use `->render()`:
+
+```php
+$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
+$cssDocument = $parser->parse();
+print $cssDocument->render();
+```
+
+If you want to format the output, pass an instance of type `\Sabberworm\CSS\OutputFormat`:
+
+```php
+$format = \Sabberworm\CSS\OutputFormat::create()
+ ->indentWithSpaces(4)->setSpaceBetweenRules("\n");
+print $cssDocument->render($format);
+```
+
+Or use one of the predefined formats:
+
+```php
+print $cssDocument->render(Sabberworm\CSS\OutputFormat::createPretty());
+print $cssDocument->render(Sabberworm\CSS\OutputFormat::createCompact());
+```
+
+To see what you can do with output formatting, look at the tests in `tests/OutputFormatTest.php`.
+
+## Examples
+
+### Example 1 (At-Rules)
+
+#### Input
+
+```css
+@charset "utf-8";
+
+@font-face {
+ font-family: "CrassRoots";
+ src: url("../media/cr.ttf");
+}
+
+html, body {
+ font-size: 1.6em;
+}
+
+@keyframes mymove {
+ from { top: 0px; }
+ to { top: 200px; }
+}
+
+```
+
+#### Structure (`var_dump()`)
+
+```php
+class Sabberworm\CSS\CSSList\Document#4 (2) {
+ protected $aContents =>
+ array(4) {
+ [0] =>
+ class Sabberworm\CSS\Property\Charset#6 (2) {
+ private $sCharset =>
+ class Sabberworm\CSS\Value\CSSString#5 (2) {
+ private $sString =>
+ string(5) "utf-8"
+ protected $iLineNo =>
+ int(1)
+ }
+ protected $iLineNo =>
+ int(1)
+ }
+ [1] =>
+ class Sabberworm\CSS\RuleSet\AtRuleSet#7 (4) {
+ private $sType =>
+ string(9) "font-face"
+ private $sArgs =>
+ string(0) ""
+ private $aRules =>
+ array(2) {
+ 'font-family' =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Rule\Rule#8 (4) {
+ private $sRule =>
+ string(11) "font-family"
+ private $mValue =>
+ class Sabberworm\CSS\Value\CSSString#9 (2) {
+ private $sString =>
+ string(10) "CrassRoots"
+ protected $iLineNo =>
+ int(4)
+ }
+ private $bIsImportant =>
+ bool(false)
+ protected $iLineNo =>
+ int(4)
+ }
+ }
+ 'src' =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Rule\Rule#10 (4) {
+ private $sRule =>
+ string(3) "src"
+ private $mValue =>
+ class Sabberworm\CSS\Value\URL#11 (2) {
+ private $oURL =>
+ class Sabberworm\CSS\Value\CSSString#12 (2) {
+ private $sString =>
+ string(15) "../media/cr.ttf"
+ protected $iLineNo =>
+ int(5)
+ }
+ protected $iLineNo =>
+ int(5)
+ }
+ private $bIsImportant =>
+ bool(false)
+ protected $iLineNo =>
+ int(5)
+ }
+ }
+ }
+ protected $iLineNo =>
+ int(3)
+ }
+ [2] =>
+ class Sabberworm\CSS\RuleSet\DeclarationBlock#13 (3) {
+ private $aSelectors =>
+ array(2) {
+ [0] =>
+ class Sabberworm\CSS\Property\Selector#14 (2) {
+ private $sSelector =>
+ string(4) "html"
+ private $iSpecificity =>
+ NULL
+ }
+ [1] =>
+ class Sabberworm\CSS\Property\Selector#15 (2) {
+ private $sSelector =>
+ string(4) "body"
+ private $iSpecificity =>
+ NULL
+ }
+ }
+ private $aRules =>
+ array(1) {
+ 'font-size' =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Rule\Rule#16 (4) {
+ private $sRule =>
+ string(9) "font-size"
+ private $mValue =>
+ class Sabberworm\CSS\Value\Size#17 (4) {
+ private $fSize =>
+ double(1.6)
+ private $sUnit =>
+ string(2) "em"
+ private $bIsColorComponent =>
+ bool(false)
+ protected $iLineNo =>
+ int(9)
+ }
+ private $bIsImportant =>
+ bool(false)
+ protected $iLineNo =>
+ int(9)
+ }
+ }
+ }
+ protected $iLineNo =>
+ int(8)
+ }
+ [3] =>
+ class Sabberworm\CSS\CSSList\KeyFrame#18 (4) {
+ private $vendorKeyFrame =>
+ string(9) "keyframes"
+ private $animationName =>
+ string(6) "mymove"
+ protected $aContents =>
+ array(2) {
+ [0] =>
+ class Sabberworm\CSS\RuleSet\DeclarationBlock#19 (3) {
+ private $aSelectors =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Property\Selector#20 (2) {
+ private $sSelector =>
+ string(4) "from"
+ private $iSpecificity =>
+ NULL
+ }
+ }
+ private $aRules =>
+ array(1) {
+ 'top' =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Rule\Rule#21 (4) {
+ private $sRule =>
+ string(3) "top"
+ private $mValue =>
+ class Sabberworm\CSS\Value\Size#22 (4) {
+ private $fSize =>
+ double(0)
+ private $sUnit =>
+ string(2) "px"
+ private $bIsColorComponent =>
+ bool(false)
+ protected $iLineNo =>
+ int(13)
+ }
+ private $bIsImportant =>
+ bool(false)
+ protected $iLineNo =>
+ int(13)
+ }
+ }
+ }
+ protected $iLineNo =>
+ int(13)
+ }
+ [1] =>
+ class Sabberworm\CSS\RuleSet\DeclarationBlock#23 (3) {
+ private $aSelectors =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Property\Selector#24 (2) {
+ private $sSelector =>
+ string(2) "to"
+ private $iSpecificity =>
+ NULL
+ }
+ }
+ private $aRules =>
+ array(1) {
+ 'top' =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Rule\Rule#25 (4) {
+ private $sRule =>
+ string(3) "top"
+ private $mValue =>
+ class Sabberworm\CSS\Value\Size#26 (4) {
+ private $fSize =>
+ double(200)
+ private $sUnit =>
+ string(2) "px"
+ private $bIsColorComponent =>
+ bool(false)
+ protected $iLineNo =>
+ int(14)
+ }
+ private $bIsImportant =>
+ bool(false)
+ protected $iLineNo =>
+ int(14)
+ }
+ }
+ }
+ protected $iLineNo =>
+ int(14)
+ }
+ }
+ protected $iLineNo =>
+ int(12)
+ }
+ }
+ protected $iLineNo =>
+ int(1)
+}
+
+```
+
+#### Output (`render()`)
+
+```css
+@charset "utf-8";
+@font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");}
+html, body {font-size: 1.6em;}
+@keyframes mymove {from {top: 0px;} to {top: 200px;}}
+```
+
+### Example 2 (Values)
+
+#### Input
+
+```css
+#header {
+ margin: 10px 2em 1cm 2%;
+ font-family: Verdana, Helvetica, "Gill Sans", sans-serif;
+ color: red !important;
+}
+
+```
+
+#### Structure (`var_dump()`)
+
+```php
+class Sabberworm\CSS\CSSList\Document#4 (2) {
+ protected $aContents =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\RuleSet\DeclarationBlock#5 (3) {
+ private $aSelectors =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Property\Selector#6 (2) {
+ private $sSelector =>
+ string(7) "#header"
+ private $iSpecificity =>
+ NULL
+ }
+ }
+ private $aRules =>
+ array(3) {
+ 'margin' =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Rule\Rule#7 (4) {
+ private $sRule =>
+ string(6) "margin"
+ private $mValue =>
+ class Sabberworm\CSS\Value\RuleValueList#12 (3) {
+ protected $aComponents =>
+ array(4) {
+ [0] =>
+ class Sabberworm\CSS\Value\Size#8 (4) {
+ private $fSize =>
+ double(10)
+ private $sUnit =>
+ string(2) "px"
+ private $bIsColorComponent =>
+ bool(false)
+ protected $iLineNo =>
+ int(2)
+ }
+ [1] =>
+ class Sabberworm\CSS\Value\Size#9 (4) {
+ private $fSize =>
+ double(2)
+ private $sUnit =>
+ string(2) "em"
+ private $bIsColorComponent =>
+ bool(false)
+ protected $iLineNo =>
+ int(2)
+ }
+ [2] =>
+ class Sabberworm\CSS\Value\Size#10 (4) {
+ private $fSize =>
+ double(1)
+ private $sUnit =>
+ string(2) "cm"
+ private $bIsColorComponent =>
+ bool(false)
+ protected $iLineNo =>
+ int(2)
+ }
+ [3] =>
+ class Sabberworm\CSS\Value\Size#11 (4) {
+ private $fSize =>
+ double(2)
+ private $sUnit =>
+ string(1) "%"
+ private $bIsColorComponent =>
+ bool(false)
+ protected $iLineNo =>
+ int(2)
+ }
+ }
+ protected $sSeparator =>
+ string(1) " "
+ protected $iLineNo =>
+ int(2)
+ }
+ private $bIsImportant =>
+ bool(false)
+ protected $iLineNo =>
+ int(2)
+ }
+ }
+ 'font-family' =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Rule\Rule#13 (4) {
+ private $sRule =>
+ string(11) "font-family"
+ private $mValue =>
+ class Sabberworm\CSS\Value\RuleValueList#15 (3) {
+ protected $aComponents =>
+ array(4) {
+ [0] =>
+ string(7) "Verdana"
+ [1] =>
+ string(9) "Helvetica"
+ [2] =>
+ class Sabberworm\CSS\Value\CSSString#14 (2) {
+ private $sString =>
+ string(9) "Gill Sans"
+ protected $iLineNo =>
+ int(3)
+ }
+ [3] =>
+ string(10) "sans-serif"
+ }
+ protected $sSeparator =>
+ string(1) ","
+ protected $iLineNo =>
+ int(3)
+ }
+ private $bIsImportant =>
+ bool(false)
+ protected $iLineNo =>
+ int(3)
+ }
+ }
+ 'color' =>
+ array(1) {
+ [0] =>
+ class Sabberworm\CSS\Rule\Rule#16 (4) {
+ private $sRule =>
+ string(5) "color"
+ private $mValue =>
+ string(3) "red"
+ private $bIsImportant =>
+ bool(true)
+ protected $iLineNo =>
+ int(4)
+ }
+ }
+ }
+ protected $iLineNo =>
+ int(1)
+ }
+ }
+ protected $iLineNo =>
+ int(1)
+}
+
+```
+
+#### Output (`render()`)
+
+```css
+#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;}
+```
+
+## Contributors/Thanks to
+
+* [oliverklee](https://github.com/oliverklee) for lots of refactorings, code modernizations and CI integrations
+* [raxbg](https://github.com/raxbg) for contributions to parse `calc`, grid lines, and various bugfixes.
+* [westonruter](https://github.com/westonruter) for bugfixes and improvements.
+* [FMCorz](https://github.com/FMCorz) for many patches and suggestions, for being able to parse comments and IE hacks (in lenient mode).
+* [Lullabot](https://github.com/Lullabot) for a patch that allows to know the line number for each parsed token.
+* [ju1ius](https://github.com/ju1ius) for the specificity parsing code and the ability to expand/compact shorthand properties.
+* [ossinkine](https://github.com/ossinkine) for a 150 time performance boost.
+* [GaryJones](https://github.com/GaryJones) for lots of input and [https://css-specificity.info/](https://css-specificity.info/).
+* [docteurklein](https://github.com/docteurklein) for output formatting and `CSSList->remove()` inspiration.
+* [nicolopignatelli](https://github.com/nicolopignatelli) for PSR-0 compatibility.
+* [diegoembarcadero](https://github.com/diegoembarcadero) for keyframe at-rule parsing.
+* [goetas](https://github.com/goetas) for @namespace at-rule support.
+* [View full list](https://github.com/sabberworm/PHP-CSS-Parser/contributors)
+
+## Misc
+
+* Legacy Support: The latest pre-PSR-0 version of this project can be checked with the `0.9.0` tag.
+* Running Tests: To run all unit tests for this project, run `composer install` to install phpunit and use `./vendor/bin/phpunit`.
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/composer.json b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/composer.json
new file mode 100644
index 000000000..e192dd56a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/composer.json
@@ -0,0 +1,69 @@
+{
+ "name": "sabberworm/php-css-parser",
+ "type": "library",
+ "description": "Parser for CSS Files written in PHP",
+ "keywords": [
+ "parser",
+ "css",
+ "stylesheet"
+ ],
+ "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Raphael Schweikert"
+ }
+ ],
+ "require": {
+ "php": ">=5.6.20",
+ "ext-iconv": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36",
+ "codacy/coverage": "^1.4"
+ },
+ "suggest": {
+ "ext-mbstring": "for parsing UTF-8 CSS"
+ },
+ "autoload": {
+ "psr-4": {
+ "Sabberworm\\CSS\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Sabberworm\\CSS\\Tests\\": "tests/"
+ }
+ },
+ "scripts": {
+ "ci": [
+ "@ci:static"
+ ],
+ "ci:php:fixer": "@php ./.phive/php-cs-fixer.phar --config=config/php-cs-fixer.php fix --dry-run -v --show-progress=dots bin src tests",
+ "ci:php:sniffer": "@php ./.phive/phpcs.phar --standard=config/phpcs.xml bin src tests",
+ "ci:php:stan": "@php ./.phive/phpstan.phar --configuration=config/phpstan.neon",
+ "ci:static": [
+ "@ci:php:fixer",
+ "@ci:php:sniffer",
+ "@ci:php:stan"
+ ],
+ "fix:php": [
+ "@fix:php:fixer",
+ "@fix:php:sniffer"
+ ],
+ "fix:php:fixer": "@php ./.phive/php-cs-fixer.phar --config=config/php-cs-fixer.php fix bin src tests",
+ "fix:php:sniffer": "@php ./.phive/phpcbf.phar --standard=config/phpcs.xml bin src tests",
+ "phpstan:baseline": "@php ./.phive/phpstan.phar --configuration=config/phpstan.neon --generate-baseline=config/phpstan-baseline.neon"
+ },
+ "scripts-descriptions": {
+ "ci": "Runs all dynamic and static code checks (i.e. currently, only the static checks).",
+ "ci:php:fixer": "Checks the code style with PHP CS Fixer.",
+ "ci:php:sniffer": "Checks the code style with PHP_CodeSniffer.",
+ "ci:php:stan": "Checks the types with PHPStan.",
+ "ci:static": "Runs all static code analysis checks for the code.",
+ "fix:php": "Autofixes all autofixable issues in the PHP code.",
+ "fix:php:fixer": "Fixes autofixable issues found by PHP CS Fixer.",
+ "fix:php:sniffer": "Fixes autofixable issues found by PHP_CodeSniffer.",
+ "phpstand:baseline": "Updates the PHPStan baseline file to match the code."
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php
new file mode 100644
index 000000000..218adb9a1
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php
@@ -0,0 +1,83 @@
+sType = $sType;
+ $this->sArgs = $sArgs;
+ }
+
+ /**
+ * @return string
+ */
+ public function atRuleName()
+ {
+ return $this->sType;
+ }
+
+ /**
+ * @return string
+ */
+ public function atRuleArgs()
+ {
+ return $this->sArgs;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $sArgs = $this->sArgs;
+ if ($sArgs) {
+ $sArgs = ' ' . $sArgs;
+ }
+ $sResult = $oOutputFormat->sBeforeAtRuleBlock;
+ $sResult .= "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{";
+ $sResult .= parent::render($oOutputFormat);
+ $sResult .= '}';
+ $sResult .= $oOutputFormat->sAfterAtRuleBlock;
+ return $sResult;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRootList()
+ {
+ return false;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php
new file mode 100644
index 000000000..fce7913eb
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php
@@ -0,0 +1,143 @@
+ $aResult
+ *
+ * @return void
+ */
+ protected function allDeclarationBlocks(array &$aResult)
+ {
+ foreach ($this->aContents as $mContent) {
+ if ($mContent instanceof DeclarationBlock) {
+ $aResult[] = $mContent;
+ } elseif ($mContent instanceof CSSBlockList) {
+ $mContent->allDeclarationBlocks($aResult);
+ }
+ }
+ }
+
+ /**
+ * @param array $aResult
+ *
+ * @return void
+ */
+ protected function allRuleSets(array &$aResult)
+ {
+ foreach ($this->aContents as $mContent) {
+ if ($mContent instanceof RuleSet) {
+ $aResult[] = $mContent;
+ } elseif ($mContent instanceof CSSBlockList) {
+ $mContent->allRuleSets($aResult);
+ }
+ }
+ }
+
+ /**
+ * @param CSSList|Rule|RuleSet|Value $oElement
+ * @param array $aResult
+ * @param string|null $sSearchString
+ * @param bool $bSearchInFunctionArguments
+ *
+ * @return void
+ */
+ protected function allValues($oElement, array &$aResult, $sSearchString = null, $bSearchInFunctionArguments = false)
+ {
+ if ($oElement instanceof CSSBlockList) {
+ foreach ($oElement->getContents() as $oContent) {
+ $this->allValues($oContent, $aResult, $sSearchString, $bSearchInFunctionArguments);
+ }
+ } elseif ($oElement instanceof RuleSet) {
+ foreach ($oElement->getRules($sSearchString) as $oRule) {
+ $this->allValues($oRule, $aResult, $sSearchString, $bSearchInFunctionArguments);
+ }
+ } elseif ($oElement instanceof Rule) {
+ $this->allValues($oElement->getValue(), $aResult, $sSearchString, $bSearchInFunctionArguments);
+ } elseif ($oElement instanceof ValueList) {
+ if ($bSearchInFunctionArguments || !($oElement instanceof CSSFunction)) {
+ foreach ($oElement->getListComponents() as $mComponent) {
+ $this->allValues($mComponent, $aResult, $sSearchString, $bSearchInFunctionArguments);
+ }
+ }
+ } else {
+ // Non-List `Value` or `CSSString` (CSS identifier)
+ $aResult[] = $oElement;
+ }
+ }
+
+ /**
+ * @param array $aResult
+ * @param string|null $sSpecificitySearch
+ *
+ * @return void
+ */
+ protected function allSelectors(array &$aResult, $sSpecificitySearch = null)
+ {
+ /** @var array $aDeclarationBlocks */
+ $aDeclarationBlocks = [];
+ $this->allDeclarationBlocks($aDeclarationBlocks);
+ foreach ($aDeclarationBlocks as $oBlock) {
+ foreach ($oBlock->getSelectors() as $oSelector) {
+ if ($sSpecificitySearch === null) {
+ $aResult[] = $oSelector;
+ } else {
+ $sComparator = '===';
+ $aSpecificitySearch = explode(' ', $sSpecificitySearch);
+ $iTargetSpecificity = $aSpecificitySearch[0];
+ if (count($aSpecificitySearch) > 1) {
+ $sComparator = $aSpecificitySearch[0];
+ $iTargetSpecificity = $aSpecificitySearch[1];
+ }
+ $iTargetSpecificity = (int)$iTargetSpecificity;
+ $iSelectorSpecificity = $oSelector->getSpecificity();
+ $bMatches = false;
+ switch ($sComparator) {
+ case '<=':
+ $bMatches = $iSelectorSpecificity <= $iTargetSpecificity;
+ break;
+ case '<':
+ $bMatches = $iSelectorSpecificity < $iTargetSpecificity;
+ break;
+ case '>=':
+ $bMatches = $iSelectorSpecificity >= $iTargetSpecificity;
+ break;
+ case '>':
+ $bMatches = $iSelectorSpecificity > $iTargetSpecificity;
+ break;
+ default:
+ $bMatches = $iSelectorSpecificity === $iTargetSpecificity;
+ break;
+ }
+ if ($bMatches) {
+ $aResult[] = $oSelector;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php
new file mode 100644
index 000000000..946740a49
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php
@@ -0,0 +1,479 @@
+
+ */
+ protected $aComments;
+
+ /**
+ * @var array
+ */
+ protected $aContents;
+
+ /**
+ * @var int
+ */
+ protected $iLineNo;
+
+ /**
+ * @param int $iLineNo
+ */
+ public function __construct($iLineNo = 0)
+ {
+ $this->aComments = [];
+ $this->aContents = [];
+ $this->iLineNo = $iLineNo;
+ }
+
+ /**
+ * @return void
+ *
+ * @throws UnexpectedTokenException
+ * @throws SourceException
+ */
+ public static function parseList(ParserState $oParserState, CSSList $oList)
+ {
+ $bIsRoot = $oList instanceof Document;
+ if (is_string($oParserState)) {
+ $oParserState = new ParserState($oParserState, Settings::create());
+ }
+ $bLenientParsing = $oParserState->getSettings()->bLenientParsing;
+ while (!$oParserState->isEnd()) {
+ $comments = $oParserState->consumeWhiteSpace();
+ $oListItem = null;
+ if ($bLenientParsing) {
+ try {
+ $oListItem = self::parseListItem($oParserState, $oList);
+ } catch (UnexpectedTokenException $e) {
+ $oListItem = false;
+ }
+ } else {
+ $oListItem = self::parseListItem($oParserState, $oList);
+ }
+ if ($oListItem === null) {
+ // List parsing finished
+ return;
+ }
+ if ($oListItem) {
+ $oListItem->setComments($comments);
+ $oList->append($oListItem);
+ }
+ $oParserState->consumeWhiteSpace();
+ }
+ if (!$bIsRoot && !$bLenientParsing) {
+ throw new SourceException("Unexpected end of document", $oParserState->currentLine());
+ }
+ }
+
+ /**
+ * @return AtRuleBlockList|KeyFrame|Charset|CSSNamespace|Import|AtRuleSet|DeclarationBlock|null|false
+ *
+ * @throws SourceException
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ private static function parseListItem(ParserState $oParserState, CSSList $oList)
+ {
+ $bIsRoot = $oList instanceof Document;
+ if ($oParserState->comes('@')) {
+ $oAtRule = self::parseAtRule($oParserState);
+ if ($oAtRule instanceof Charset) {
+ if (!$bIsRoot) {
+ throw new UnexpectedTokenException(
+ '@charset may only occur in root document',
+ '',
+ 'custom',
+ $oParserState->currentLine()
+ );
+ }
+ if (count($oList->getContents()) > 0) {
+ throw new UnexpectedTokenException(
+ '@charset must be the first parseable token in a document',
+ '',
+ 'custom',
+ $oParserState->currentLine()
+ );
+ }
+ $oParserState->setCharset($oAtRule->getCharset()->getString());
+ }
+ return $oAtRule;
+ } elseif ($oParserState->comes('}')) {
+ if (!$oParserState->getSettings()->bLenientParsing) {
+ throw new UnexpectedTokenException('CSS selector', '}', 'identifier', $oParserState->currentLine());
+ } else {
+ if ($bIsRoot) {
+ if ($oParserState->getSettings()->bLenientParsing) {
+ return DeclarationBlock::parse($oParserState);
+ } else {
+ throw new SourceException("Unopened {", $oParserState->currentLine());
+ }
+ } else {
+ return null;
+ }
+ }
+ } else {
+ return DeclarationBlock::parse($oParserState, $oList);
+ }
+ }
+
+ /**
+ * @param ParserState $oParserState
+ *
+ * @return AtRuleBlockList|KeyFrame|Charset|CSSNamespace|Import|AtRuleSet|null
+ *
+ * @throws SourceException
+ * @throws UnexpectedTokenException
+ * @throws UnexpectedEOFException
+ */
+ private static function parseAtRule(ParserState $oParserState)
+ {
+ $oParserState->consume('@');
+ $sIdentifier = $oParserState->parseIdentifier();
+ $iIdentifierLineNum = $oParserState->currentLine();
+ $oParserState->consumeWhiteSpace();
+ if ($sIdentifier === 'import') {
+ $oLocation = URL::parse($oParserState);
+ $oParserState->consumeWhiteSpace();
+ $sMediaQuery = null;
+ if (!$oParserState->comes(';')) {
+ $sMediaQuery = trim($oParserState->consumeUntil([';', ParserState::EOF]));
+ }
+ $oParserState->consumeUntil([';', ParserState::EOF], true, true);
+ return new Import($oLocation, $sMediaQuery ?: null, $iIdentifierLineNum);
+ } elseif ($sIdentifier === 'charset') {
+ $sCharset = CSSString::parse($oParserState);
+ $oParserState->consumeWhiteSpace();
+ $oParserState->consumeUntil([';', ParserState::EOF], true, true);
+ return new Charset($sCharset, $iIdentifierLineNum);
+ } elseif (self::identifierIs($sIdentifier, 'keyframes')) {
+ $oResult = new KeyFrame($iIdentifierLineNum);
+ $oResult->setVendorKeyFrame($sIdentifier);
+ $oResult->setAnimationName(trim($oParserState->consumeUntil('{', false, true)));
+ CSSList::parseList($oParserState, $oResult);
+ if ($oParserState->comes('}')) {
+ $oParserState->consume('}');
+ }
+ return $oResult;
+ } elseif ($sIdentifier === 'namespace') {
+ $sPrefix = null;
+ $mUrl = Value::parsePrimitiveValue($oParserState);
+ if (!$oParserState->comes(';')) {
+ $sPrefix = $mUrl;
+ $mUrl = Value::parsePrimitiveValue($oParserState);
+ }
+ $oParserState->consumeUntil([';', ParserState::EOF], true, true);
+ if ($sPrefix !== null && !is_string($sPrefix)) {
+ throw new UnexpectedTokenException('Wrong namespace prefix', $sPrefix, 'custom', $iIdentifierLineNum);
+ }
+ if (!($mUrl instanceof CSSString || $mUrl instanceof URL)) {
+ throw new UnexpectedTokenException(
+ 'Wrong namespace url of invalid type',
+ $mUrl,
+ 'custom',
+ $iIdentifierLineNum
+ );
+ }
+ return new CSSNamespace($mUrl, $sPrefix, $iIdentifierLineNum);
+ } else {
+ // Unknown other at rule (font-face or such)
+ $sArgs = trim($oParserState->consumeUntil('{', false, true));
+ if (substr_count($sArgs, "(") != substr_count($sArgs, ")")) {
+ if ($oParserState->getSettings()->bLenientParsing) {
+ return null;
+ } else {
+ throw new SourceException("Unmatched brace count in media query", $oParserState->currentLine());
+ }
+ }
+ $bUseRuleSet = true;
+ foreach (explode('/', AtRule::BLOCK_RULES) as $sBlockRuleName) {
+ if (self::identifierIs($sIdentifier, $sBlockRuleName)) {
+ $bUseRuleSet = false;
+ break;
+ }
+ }
+ if ($bUseRuleSet) {
+ $oAtRule = new AtRuleSet($sIdentifier, $sArgs, $iIdentifierLineNum);
+ RuleSet::parseRuleSet($oParserState, $oAtRule);
+ } else {
+ $oAtRule = new AtRuleBlockList($sIdentifier, $sArgs, $iIdentifierLineNum);
+ CSSList::parseList($oParserState, $oAtRule);
+ if ($oParserState->comes('}')) {
+ $oParserState->consume('}');
+ }
+ }
+ return $oAtRule;
+ }
+ }
+
+ /**
+ * Tests an identifier for a given value. Since identifiers are all keywords, they can be vendor-prefixed.
+ * We need to check for these versions too.
+ *
+ * @param string $sIdentifier
+ * @param string $sMatch
+ *
+ * @return bool
+ */
+ private static function identifierIs($sIdentifier, $sMatch)
+ {
+ return (strcasecmp($sIdentifier, $sMatch) === 0)
+ ?: preg_match("/^(-\\w+-)?$sMatch$/i", $sIdentifier) === 1;
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+
+ /**
+ * Prepends an item to the list of contents.
+ *
+ * @param RuleSet|CSSList|Import|Charset $oItem
+ *
+ * @return void
+ */
+ public function prepend($oItem)
+ {
+ array_unshift($this->aContents, $oItem);
+ }
+
+ /**
+ * Appends an item to tje list of contents.
+ *
+ * @param RuleSet|CSSList|Import|Charset $oItem
+ *
+ * @return void
+ */
+ public function append($oItem)
+ {
+ $this->aContents[] = $oItem;
+ }
+
+ /**
+ * Splices the list of contents.
+ *
+ * @param int $iOffset
+ * @param int $iLength
+ * @param array $mReplacement
+ *
+ * @return void
+ */
+ public function splice($iOffset, $iLength = null, $mReplacement = null)
+ {
+ array_splice($this->aContents, $iOffset, $iLength, $mReplacement);
+ }
+
+ /**
+ * Removes an item from the CSS list.
+ *
+ * @param RuleSet|Import|Charset|CSSList $oItemToRemove
+ * May be a RuleSet (most likely a DeclarationBlock), a Import,
+ * a Charset or another CSSList (most likely a MediaQuery)
+ *
+ * @return bool whether the item was removed
+ */
+ public function remove($oItemToRemove)
+ {
+ $iKey = array_search($oItemToRemove, $this->aContents, true);
+ if ($iKey !== false) {
+ unset($this->aContents[$iKey]);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Replaces an item from the CSS list.
+ *
+ * @param RuleSet|Import|Charset|CSSList $oOldItem
+ * May be a `RuleSet` (most likely a `DeclarationBlock`), an `Import`, a `Charset`
+ * or another `CSSList` (most likely a `MediaQuery`)
+ *
+ * @return bool
+ */
+ public function replace($oOldItem, $mNewItem)
+ {
+ $iKey = array_search($oOldItem, $this->aContents, true);
+ if ($iKey !== false) {
+ if (is_array($mNewItem)) {
+ array_splice($this->aContents, $iKey, 1, $mNewItem);
+ } else {
+ array_splice($this->aContents, $iKey, 1, [$mNewItem]);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param array $aContents
+ */
+ public function setContents(array $aContents)
+ {
+ $this->aContents = [];
+ foreach ($aContents as $content) {
+ $this->append($content);
+ }
+ }
+
+ /**
+ * Removes a declaration block from the CSS list if it matches all given selectors.
+ *
+ * @param DeclarationBlock|array|string $mSelector the selectors to match
+ * @param bool $bRemoveAll whether to stop at the first declaration block found or remove all blocks
+ *
+ * @return void
+ */
+ public function removeDeclarationBlockBySelector($mSelector, $bRemoveAll = false)
+ {
+ if ($mSelector instanceof DeclarationBlock) {
+ $mSelector = $mSelector->getSelectors();
+ }
+ if (!is_array($mSelector)) {
+ $mSelector = explode(',', $mSelector);
+ }
+ foreach ($mSelector as $iKey => &$mSel) {
+ if (!($mSel instanceof Selector)) {
+ if (!Selector::isValid($mSel)) {
+ throw new UnexpectedTokenException(
+ "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
+ $mSel,
+ "custom"
+ );
+ }
+ $mSel = new Selector($mSel);
+ }
+ }
+ foreach ($this->aContents as $iKey => $mItem) {
+ if (!($mItem instanceof DeclarationBlock)) {
+ continue;
+ }
+ if ($mItem->getSelectors() == $mSelector) {
+ unset($this->aContents[$iKey]);
+ if (!$bRemoveAll) {
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $sResult = '';
+ $bIsFirst = true;
+ $oNextLevel = $oOutputFormat;
+ if (!$this->isRootList()) {
+ $oNextLevel = $oOutputFormat->nextLevel();
+ }
+ foreach ($this->aContents as $oContent) {
+ $sRendered = $oOutputFormat->safely(function () use ($oNextLevel, $oContent) {
+ return $oContent->render($oNextLevel);
+ });
+ if ($sRendered === null) {
+ continue;
+ }
+ if ($bIsFirst) {
+ $bIsFirst = false;
+ $sResult .= $oNextLevel->spaceBeforeBlocks();
+ } else {
+ $sResult .= $oNextLevel->spaceBetweenBlocks();
+ }
+ $sResult .= $sRendered;
+ }
+
+ if (!$bIsFirst) {
+ // Had some output
+ $sResult .= $oOutputFormat->spaceAfterBlocks();
+ }
+
+ return $sResult;
+ }
+
+ /**
+ * Return true if the list can not be further outdented. Only important when rendering.
+ *
+ * @return bool
+ */
+ abstract public function isRootList();
+
+ /**
+ * @return array
+ */
+ public function getContents()
+ {
+ return $this->aContents;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function addComments(array $aComments)
+ {
+ $this->aComments = array_merge($this->aComments, $aComments);
+ }
+
+ /**
+ * @return array
+ */
+ public function getComments()
+ {
+ return $this->aComments;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function setComments(array $aComments)
+ {
+ $this->aComments = $aComments;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/Document.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/Document.php
new file mode 100644
index 000000000..91ab2c6b2
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/Document.php
@@ -0,0 +1,172 @@
+currentLine());
+ CSSList::parseList($oParserState, $oDocument);
+ return $oDocument;
+ }
+
+ /**
+ * Gets all `DeclarationBlock` objects recursively.
+ *
+ * @return array
+ */
+ public function getAllDeclarationBlocks()
+ {
+ /** @var array $aResult */
+ $aResult = [];
+ $this->allDeclarationBlocks($aResult);
+ return $aResult;
+ }
+
+ /**
+ * Gets all `DeclarationBlock` objects recursively.
+ *
+ * @return array
+ *
+ * @deprecated will be removed in version 9.0; use `getAllDeclarationBlocks()` instead
+ */
+ public function getAllSelectors()
+ {
+ return $this->getAllDeclarationBlocks();
+ }
+
+ /**
+ * Returns all `RuleSet` objects found recursively in the tree.
+ *
+ * @return array
+ */
+ public function getAllRuleSets()
+ {
+ /** @var array $aResult */
+ $aResult = [];
+ $this->allRuleSets($aResult);
+ return $aResult;
+ }
+
+ /**
+ * Returns all `Value` objects found recursively in the tree.
+ *
+ * @param CSSList|RuleSet|string $mElement
+ * the `CSSList` or `RuleSet` to start the search from (defaults to the whole document).
+ * If a string is given, it is used as rule name filter.
+ * @param bool $bSearchInFunctionArguments whether to also return Value objects used as Function arguments.
+ *
+ * @return array
+ *
+ * @see RuleSet->getRules()
+ */
+ public function getAllValues($mElement = null, $bSearchInFunctionArguments = false)
+ {
+ $sSearchString = null;
+ if ($mElement === null) {
+ $mElement = $this;
+ } elseif (is_string($mElement)) {
+ $sSearchString = $mElement;
+ $mElement = $this;
+ }
+ /** @var array $aResult */
+ $aResult = [];
+ $this->allValues($mElement, $aResult, $sSearchString, $bSearchInFunctionArguments);
+ return $aResult;
+ }
+
+ /**
+ * Returns all `Selector` objects found recursively in the tree.
+ *
+ * Note that this does not yield the full `DeclarationBlock` that the selector belongs to
+ * (and, currently, there is no way to get to that).
+ *
+ * @param string|null $sSpecificitySearch
+ * An optional filter by specificity.
+ * May contain a comparison operator and a number or just a number (defaults to "==").
+ *
+ * @return array
+ * @example `getSelectorsBySpecificity('>= 100')`
+ *
+ */
+ public function getSelectorsBySpecificity($sSpecificitySearch = null)
+ {
+ /** @var array $aResult */
+ $aResult = [];
+ $this->allSelectors($aResult, $sSpecificitySearch);
+ return $aResult;
+ }
+
+ /**
+ * Expands all shorthand properties to their long value.
+ *
+ * @return void
+ */
+ public function expandShorthands()
+ {
+ foreach ($this->getAllDeclarationBlocks() as $oDeclaration) {
+ $oDeclaration->expandShorthands();
+ }
+ }
+
+ /**
+ * Create shorthands properties whenever possible.
+ *
+ * @return void
+ */
+ public function createShorthands()
+ {
+ foreach ($this->getAllDeclarationBlocks() as $oDeclaration) {
+ $oDeclaration->createShorthands();
+ }
+ }
+
+ /**
+ * Overrides `render()` to make format argument optional.
+ *
+ * @param OutputFormat|null $oOutputFormat
+ *
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat = null)
+ {
+ if ($oOutputFormat === null) {
+ $oOutputFormat = new OutputFormat();
+ }
+ return parent::render($oOutputFormat);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRootList()
+ {
+ return true;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php
new file mode 100644
index 000000000..d9420e9c0
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php
@@ -0,0 +1,104 @@
+vendorKeyFrame = null;
+ $this->animationName = null;
+ }
+
+ /**
+ * @param string $vendorKeyFrame
+ */
+ public function setVendorKeyFrame($vendorKeyFrame)
+ {
+ $this->vendorKeyFrame = $vendorKeyFrame;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getVendorKeyFrame()
+ {
+ return $this->vendorKeyFrame;
+ }
+
+ /**
+ * @param string $animationName
+ */
+ public function setAnimationName($animationName)
+ {
+ $this->animationName = $animationName;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getAnimationName()
+ {
+ return $this->animationName;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $sResult = "@{$this->vendorKeyFrame} {$this->animationName}{$oOutputFormat->spaceBeforeOpeningBrace()}{";
+ $sResult .= parent::render($oOutputFormat);
+ $sResult .= '}';
+ return $sResult;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRootList()
+ {
+ return false;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function atRuleName()
+ {
+ return $this->vendorKeyFrame;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function atRuleArgs()
+ {
+ return $this->animationName;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Comment.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Comment.php
new file mode 100644
index 000000000..6128d7498
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Comment.php
@@ -0,0 +1,71 @@
+sComment = $sComment;
+ $this->iLineNo = $iLineNo;
+ }
+
+ /**
+ * @return string
+ */
+ public function getComment()
+ {
+ return $this->sComment;
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+
+ /**
+ * @param string $sComment
+ *
+ * @return void
+ */
+ public function setComment($sComment)
+ {
+ $this->sComment = $sComment;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ return '/*' . $this->sComment . '*/';
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php
new file mode 100644
index 000000000..5e450bfb3
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php
@@ -0,0 +1,25 @@
+ $aComments
+ *
+ * @return void
+ */
+ public function addComments(array $aComments);
+
+ /**
+ * @return array
+ */
+ public function getComments();
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function setComments(array $aComments);
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormat.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormat.php
new file mode 100644
index 000000000..595d30643
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormat.php
@@ -0,0 +1,334 @@
+set('Space*Rules', "\n");`)
+ */
+ public $sSpaceAfterRuleName = ' ';
+
+ /**
+ * @var string
+ */
+ public $sSpaceBeforeRules = '';
+
+ /**
+ * @var string
+ */
+ public $sSpaceAfterRules = '';
+
+ /**
+ * @var string
+ */
+ public $sSpaceBetweenRules = '';
+
+ /**
+ * @var string
+ */
+ public $sSpaceBeforeBlocks = '';
+
+ /**
+ * @var string
+ */
+ public $sSpaceAfterBlocks = '';
+
+ /**
+ * @var string
+ */
+ public $sSpaceBetweenBlocks = "\n";
+
+ /**
+ * Content injected in and around at-rule blocks.
+ *
+ * @var string
+ */
+ public $sBeforeAtRuleBlock = '';
+
+ /**
+ * @var string
+ */
+ public $sAfterAtRuleBlock = '';
+
+ /**
+ * This is what’s printed before and after the comma if a declaration block contains multiple selectors.
+ *
+ * @var string
+ */
+ public $sSpaceBeforeSelectorSeparator = '';
+
+ /**
+ * @var string
+ */
+ public $sSpaceAfterSelectorSeparator = ' ';
+
+ /**
+ * This is what’s printed after the comma of value lists
+ *
+ * @var string
+ */
+ public $sSpaceBeforeListArgumentSeparator = '';
+
+ /**
+ * @var string
+ */
+ public $sSpaceAfterListArgumentSeparator = '';
+
+ /**
+ * @var string
+ */
+ public $sSpaceBeforeOpeningBrace = ' ';
+
+ /**
+ * Content injected in and around declaration blocks.
+ *
+ * @var string
+ */
+ public $sBeforeDeclarationBlock = '';
+
+ /**
+ * @var string
+ */
+ public $sAfterDeclarationBlockSelectors = '';
+
+ /**
+ * @var string
+ */
+ public $sAfterDeclarationBlock = '';
+
+ /**
+ * Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings.
+ *
+ * @var string
+ */
+ public $sIndentation = "\t";
+
+ /**
+ * Output exceptions.
+ *
+ * @var bool
+ */
+ public $bIgnoreExceptions = false;
+
+ /**
+ * @var OutputFormatter|null
+ */
+ private $oFormatter = null;
+
+ /**
+ * @var OutputFormat|null
+ */
+ private $oNextLevelFormat = null;
+
+ /**
+ * @var int
+ */
+ private $iIndentationLevel = 0;
+
+ public function __construct()
+ {
+ }
+
+ /**
+ * @param string $sName
+ *
+ * @return string|null
+ */
+ public function get($sName)
+ {
+ $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i'];
+ foreach ($aVarPrefixes as $sPrefix) {
+ $sFieldName = $sPrefix . ucfirst($sName);
+ if (isset($this->$sFieldName)) {
+ return $this->$sFieldName;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param array|string $aNames
+ * @param mixed $mValue
+ *
+ * @return self|false
+ */
+ public function set($aNames, $mValue)
+ {
+ $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i'];
+ if (is_string($aNames) && strpos($aNames, '*') !== false) {
+ $aNames =
+ [
+ str_replace('*', 'Before', $aNames),
+ str_replace('*', 'Between', $aNames),
+ str_replace('*', 'After', $aNames),
+ ];
+ } elseif (!is_array($aNames)) {
+ $aNames = [$aNames];
+ }
+ foreach ($aVarPrefixes as $sPrefix) {
+ $bDidReplace = false;
+ foreach ($aNames as $sName) {
+ $sFieldName = $sPrefix . ucfirst($sName);
+ if (isset($this->$sFieldName)) {
+ $this->$sFieldName = $mValue;
+ $bDidReplace = true;
+ }
+ }
+ if ($bDidReplace) {
+ return $this;
+ }
+ }
+ // Break the chain so the user knows this option is invalid
+ return false;
+ }
+
+ /**
+ * @param string $sMethodName
+ * @param array $aArguments
+ *
+ * @return mixed
+ *
+ * @throws \Exception
+ */
+ public function __call($sMethodName, array $aArguments)
+ {
+ if (strpos($sMethodName, 'set') === 0) {
+ return $this->set(substr($sMethodName, 3), $aArguments[0]);
+ } elseif (strpos($sMethodName, 'get') === 0) {
+ return $this->get(substr($sMethodName, 3));
+ } elseif (method_exists(OutputFormatter::class, $sMethodName)) {
+ return call_user_func_array([$this->getFormatter(), $sMethodName], $aArguments);
+ } else {
+ throw new \Exception('Unknown OutputFormat method called: ' . $sMethodName);
+ }
+ }
+
+ /**
+ * @param int $iNumber
+ *
+ * @return self
+ */
+ public function indentWithTabs($iNumber = 1)
+ {
+ return $this->setIndentation(str_repeat("\t", $iNumber));
+ }
+
+ /**
+ * @param int $iNumber
+ *
+ * @return self
+ */
+ public function indentWithSpaces($iNumber = 2)
+ {
+ return $this->setIndentation(str_repeat(" ", $iNumber));
+ }
+
+ /**
+ * @return OutputFormat
+ */
+ public function nextLevel()
+ {
+ if ($this->oNextLevelFormat === null) {
+ $this->oNextLevelFormat = clone $this;
+ $this->oNextLevelFormat->iIndentationLevel++;
+ $this->oNextLevelFormat->oFormatter = null;
+ }
+ return $this->oNextLevelFormat;
+ }
+
+ /**
+ * @return void
+ */
+ public function beLenient()
+ {
+ $this->bIgnoreExceptions = true;
+ }
+
+ /**
+ * @return OutputFormatter
+ */
+ public function getFormatter()
+ {
+ if ($this->oFormatter === null) {
+ $this->oFormatter = new OutputFormatter($this);
+ }
+ return $this->oFormatter;
+ }
+
+ /**
+ * @return int
+ */
+ public function level()
+ {
+ return $this->iIndentationLevel;
+ }
+
+ /**
+ * Creates an instance of this class without any particular formatting settings.
+ *
+ * @return self
+ */
+ public static function create()
+ {
+ return new OutputFormat();
+ }
+
+ /**
+ * Creates an instance of this class with a preset for compact formatting.
+ *
+ * @return self
+ */
+ public static function createCompact()
+ {
+ $format = self::create();
+ $format->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('')
+ ->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator('');
+ return $format;
+ }
+
+ /**
+ * Creates an instance of this class with a preset for pretty formatting.
+ *
+ * @return self
+ */
+ public static function createPretty()
+ {
+ $format = self::create();
+ $format->set('Space*Rules', "\n")->set('Space*Blocks', "\n")
+ ->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', ['default' => '', ',' => ' ']);
+ return $format;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormatter.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormatter.php
new file mode 100644
index 000000000..535feca7f
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormatter.php
@@ -0,0 +1,231 @@
+oFormat = $oFormat;
+ }
+
+ /**
+ * @param string $sName
+ * @param string|null $sType
+ *
+ * @return string
+ */
+ public function space($sName, $sType = null)
+ {
+ $sSpaceString = $this->oFormat->get("Space$sName");
+ // If $sSpaceString is an array, we have multiple values configured
+ // depending on the type of object the space applies to
+ if (is_array($sSpaceString)) {
+ if ($sType !== null && isset($sSpaceString[$sType])) {
+ $sSpaceString = $sSpaceString[$sType];
+ } else {
+ $sSpaceString = reset($sSpaceString);
+ }
+ }
+ return $this->prepareSpace($sSpaceString);
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceAfterRuleName()
+ {
+ return $this->space('AfterRuleName');
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceBeforeRules()
+ {
+ return $this->space('BeforeRules');
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceAfterRules()
+ {
+ return $this->space('AfterRules');
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceBetweenRules()
+ {
+ return $this->space('BetweenRules');
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceBeforeBlocks()
+ {
+ return $this->space('BeforeBlocks');
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceAfterBlocks()
+ {
+ return $this->space('AfterBlocks');
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceBetweenBlocks()
+ {
+ return $this->space('BetweenBlocks');
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceBeforeSelectorSeparator()
+ {
+ return $this->space('BeforeSelectorSeparator');
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceAfterSelectorSeparator()
+ {
+ return $this->space('AfterSelectorSeparator');
+ }
+
+ /**
+ * @param string $sSeparator
+ *
+ * @return string
+ */
+ public function spaceBeforeListArgumentSeparator($sSeparator)
+ {
+ return $this->space('BeforeListArgumentSeparator', $sSeparator);
+ }
+
+ /**
+ * @param string $sSeparator
+ *
+ * @return string
+ */
+ public function spaceAfterListArgumentSeparator($sSeparator)
+ {
+ return $this->space('AfterListArgumentSeparator', $sSeparator);
+ }
+
+ /**
+ * @return string
+ */
+ public function spaceBeforeOpeningBrace()
+ {
+ return $this->space('BeforeOpeningBrace');
+ }
+
+ /**
+ * Runs the given code, either swallowing or passing exceptions, depending on the `bIgnoreExceptions` setting.
+ *
+ * @param string $cCode the name of the function to call
+ *
+ * @return string|null
+ */
+ public function safely($cCode)
+ {
+ if ($this->oFormat->get('IgnoreExceptions')) {
+ // If output exceptions are ignored, run the code with exception guards
+ try {
+ return $cCode();
+ } catch (OutputException $e) {
+ return null;
+ } // Do nothing
+ } else {
+ // Run the code as-is
+ return $cCode();
+ }
+ }
+
+ /**
+ * Clone of the `implode` function, but calls `render` with the current output format instead of `__toString()`.
+ *
+ * @param string $sSeparator
+ * @param array $aValues
+ * @param bool $bIncreaseLevel
+ *
+ * @return string
+ */
+ public function implode($sSeparator, array $aValues, $bIncreaseLevel = false)
+ {
+ $sResult = '';
+ $oFormat = $this->oFormat;
+ if ($bIncreaseLevel) {
+ $oFormat = $oFormat->nextLevel();
+ }
+ $bIsFirst = true;
+ foreach ($aValues as $mValue) {
+ if ($bIsFirst) {
+ $bIsFirst = false;
+ } else {
+ $sResult .= $sSeparator;
+ }
+ if ($mValue instanceof Renderable) {
+ $sResult .= $mValue->render($oFormat);
+ } else {
+ $sResult .= $mValue;
+ }
+ }
+ return $sResult;
+ }
+
+ /**
+ * @param string $sString
+ *
+ * @return string
+ */
+ public function removeLastSemicolon($sString)
+ {
+ if ($this->oFormat->get('SemicolonAfterLastRule')) {
+ return $sString;
+ }
+ $sString = explode(';', $sString);
+ if (count($sString) < 2) {
+ return $sString[0];
+ }
+ $sLast = array_pop($sString);
+ $sNextToLast = array_pop($sString);
+ array_push($sString, $sNextToLast . $sLast);
+ return implode(';', $sString);
+ }
+
+ /**
+ * @param string $sSpaceString
+ *
+ * @return string
+ */
+ private function prepareSpace($sSpaceString)
+ {
+ return str_replace("\n", "\n" . $this->indent(), $sSpaceString);
+ }
+
+ /**
+ * @return string
+ */
+ private function indent()
+ {
+ return str_repeat($this->oFormat->sIndentation, $this->oFormat->level());
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parser.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parser.php
new file mode 100644
index 000000000..f3b0493a5
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parser.php
@@ -0,0 +1,60 @@
+oParserState = new ParserState($sText, $oParserSettings, $iLineNo);
+ }
+
+ /**
+ * @param string $sCharset
+ *
+ * @return void
+ */
+ public function setCharset($sCharset)
+ {
+ $this->oParserState->setCharset($sCharset);
+ }
+
+ /**
+ * @return void
+ */
+ public function getCharset()
+ {
+ // Note: The `return` statement is missing here. This is a bug that needs to be fixed.
+ $this->oParserState->getCharset();
+ }
+
+ /**
+ * @return Document
+ *
+ * @throws SourceException
+ */
+ public function parse()
+ {
+ return Document::parse($this->oParserState);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php
new file mode 100644
index 000000000..9bfbc75fb
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php
@@ -0,0 +1,18 @@
+
+ */
+ private $aText;
+
+ /**
+ * @var int
+ */
+ private $iCurrentPosition;
+
+ /**
+ * @var string
+ */
+ private $sCharset;
+
+ /**
+ * @var int
+ */
+ private $iLength;
+
+ /**
+ * @var int
+ */
+ private $iLineNo;
+
+ /**
+ * @param string $sText
+ * @param int $iLineNo
+ */
+ public function __construct($sText, Settings $oParserSettings, $iLineNo = 1)
+ {
+ $this->oParserSettings = $oParserSettings;
+ $this->sText = $sText;
+ $this->iCurrentPosition = 0;
+ $this->iLineNo = $iLineNo;
+ $this->setCharset($this->oParserSettings->sDefaultCharset);
+ }
+
+ /**
+ * @param string $sCharset
+ *
+ * @return void
+ */
+ public function setCharset($sCharset)
+ {
+ $this->sCharset = $sCharset;
+ $this->aText = $this->strsplit($this->sText);
+ if (is_array($this->aText)) {
+ $this->iLength = count($this->aText);
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getCharset()
+ {
+ return $this->sCharset;
+ }
+
+ /**
+ * @return int
+ */
+ public function currentLine()
+ {
+ return $this->iLineNo;
+ }
+
+ /**
+ * @return int
+ */
+ public function currentColumn()
+ {
+ return $this->iCurrentPosition;
+ }
+
+ /**
+ * @return Settings
+ */
+ public function getSettings()
+ {
+ return $this->oParserSettings;
+ }
+
+ /**
+ * @param bool $bIgnoreCase
+ *
+ * @return string
+ *
+ * @throws UnexpectedTokenException
+ */
+ public function parseIdentifier($bIgnoreCase = true)
+ {
+ $sResult = $this->parseCharacter(true);
+ if ($sResult === null) {
+ throw new UnexpectedTokenException($sResult, $this->peek(5), 'identifier', $this->iLineNo);
+ }
+ $sCharacter = null;
+ while (($sCharacter = $this->parseCharacter(true)) !== null) {
+ if (preg_match('/[a-zA-Z0-9\x{00A0}-\x{FFFF}_-]/Sux', $sCharacter)) {
+ $sResult .= $sCharacter;
+ } else {
+ $sResult .= '\\' . $sCharacter;
+ }
+ }
+ if ($bIgnoreCase) {
+ $sResult = $this->strtolower($sResult);
+ }
+ return $sResult;
+ }
+
+ /**
+ * @param bool $bIsForIdentifier
+ *
+ * @return string|null
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public function parseCharacter($bIsForIdentifier)
+ {
+ if ($this->peek() === '\\') {
+ if (
+ $bIsForIdentifier && $this->oParserSettings->bLenientParsing
+ && ($this->comes('\0') || $this->comes('\9'))
+ ) {
+ // Non-strings can contain \0 or \9 which is an IE hack supported in lenient parsing.
+ return null;
+ }
+ $this->consume('\\');
+ if ($this->comes('\n') || $this->comes('\r')) {
+ return '';
+ }
+ if (preg_match('/[0-9a-fA-F]/Su', $this->peek()) === 0) {
+ return $this->consume(1);
+ }
+ $sUnicode = $this->consumeExpression('/^[0-9a-fA-F]{1,6}/u', 6);
+ if ($this->strlen($sUnicode) < 6) {
+ // Consume whitespace after incomplete unicode escape
+ if (preg_match('/\\s/isSu', $this->peek())) {
+ if ($this->comes('\r\n')) {
+ $this->consume(2);
+ } else {
+ $this->consume(1);
+ }
+ }
+ }
+ $iUnicode = intval($sUnicode, 16);
+ $sUtf32 = "";
+ for ($i = 0; $i < 4; ++$i) {
+ $sUtf32 .= chr($iUnicode & 0xff);
+ $iUnicode = $iUnicode >> 8;
+ }
+ return iconv('utf-32le', $this->sCharset, $sUtf32);
+ }
+ if ($bIsForIdentifier) {
+ $peek = ord($this->peek());
+ // Ranges: a-z A-Z 0-9 - _
+ if (
+ ($peek >= 97 && $peek <= 122)
+ || ($peek >= 65 && $peek <= 90)
+ || ($peek >= 48 && $peek <= 57)
+ || ($peek === 45)
+ || ($peek === 95)
+ || ($peek > 0xa1)
+ ) {
+ return $this->consume(1);
+ }
+ } else {
+ return $this->consume(1);
+ }
+ return null;
+ }
+
+ /**
+ * @return array|void
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public function consumeWhiteSpace()
+ {
+ $comments = [];
+ do {
+ while (preg_match('/\\s/isSu', $this->peek()) === 1) {
+ $this->consume(1);
+ }
+ if ($this->oParserSettings->bLenientParsing) {
+ try {
+ $oComment = $this->consumeComment();
+ } catch (UnexpectedEOFException $e) {
+ $this->iCurrentPosition = $this->iLength;
+ return;
+ }
+ } else {
+ $oComment = $this->consumeComment();
+ }
+ if ($oComment !== false) {
+ $comments[] = $oComment;
+ }
+ } while ($oComment !== false);
+ return $comments;
+ }
+
+ /**
+ * @param string $sString
+ * @param bool $bCaseInsensitive
+ *
+ * @return bool
+ */
+ public function comes($sString, $bCaseInsensitive = false)
+ {
+ $sPeek = $this->peek(strlen($sString));
+ return ($sPeek == '')
+ ? false
+ : $this->streql($sPeek, $sString, $bCaseInsensitive);
+ }
+
+ /**
+ * @param int $iLength
+ * @param int $iOffset
+ *
+ * @return string
+ */
+ public function peek($iLength = 1, $iOffset = 0)
+ {
+ $iOffset += $this->iCurrentPosition;
+ if ($iOffset >= $this->iLength) {
+ return '';
+ }
+ return $this->substr($iOffset, $iLength);
+ }
+
+ /**
+ * @param int $mValue
+ *
+ * @return string
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public function consume($mValue = 1)
+ {
+ if (is_string($mValue)) {
+ $iLineCount = substr_count($mValue, "\n");
+ $iLength = $this->strlen($mValue);
+ if (!$this->streql($this->substr($this->iCurrentPosition, $iLength), $mValue)) {
+ throw new UnexpectedTokenException($mValue, $this->peek(max($iLength, 5)), $this->iLineNo);
+ }
+ $this->iLineNo += $iLineCount;
+ $this->iCurrentPosition += $this->strlen($mValue);
+ return $mValue;
+ } else {
+ if ($this->iCurrentPosition + $mValue > $this->iLength) {
+ throw new UnexpectedEOFException($mValue, $this->peek(5), 'count', $this->iLineNo);
+ }
+ $sResult = $this->substr($this->iCurrentPosition, $mValue);
+ $iLineCount = substr_count($sResult, "\n");
+ $this->iLineNo += $iLineCount;
+ $this->iCurrentPosition += $mValue;
+ return $sResult;
+ }
+ }
+
+ /**
+ * @param string $mExpression
+ * @param int|null $iMaxLength
+ *
+ * @return string
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public function consumeExpression($mExpression, $iMaxLength = null)
+ {
+ $aMatches = null;
+ $sInput = $iMaxLength !== null ? $this->peek($iMaxLength) : $this->inputLeft();
+ if (preg_match($mExpression, $sInput, $aMatches, PREG_OFFSET_CAPTURE) === 1) {
+ return $this->consume($aMatches[0][0]);
+ }
+ throw new UnexpectedTokenException($mExpression, $this->peek(5), 'expression', $this->iLineNo);
+ }
+
+ /**
+ * @return Comment|false
+ */
+ public function consumeComment()
+ {
+ $mComment = false;
+ if ($this->comes('/*')) {
+ $iLineNo = $this->iLineNo;
+ $this->consume(1);
+ $mComment = '';
+ while (($char = $this->consume(1)) !== '') {
+ $mComment .= $char;
+ if ($this->comes('*/')) {
+ $this->consume(2);
+ break;
+ }
+ }
+ }
+
+ if ($mComment !== false) {
+ // We skip the * which was included in the comment.
+ return new Comment(substr($mComment, 1), $iLineNo);
+ }
+
+ return $mComment;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isEnd()
+ {
+ return $this->iCurrentPosition >= $this->iLength;
+ }
+
+ /**
+ * @param array|string $aEnd
+ * @param string $bIncludeEnd
+ * @param string $consumeEnd
+ * @param array $comments
+ *
+ * @return string
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public function consumeUntil($aEnd, $bIncludeEnd = false, $consumeEnd = false, array &$comments = [])
+ {
+ $aEnd = is_array($aEnd) ? $aEnd : [$aEnd];
+ $out = '';
+ $start = $this->iCurrentPosition;
+
+ while (!$this->isEnd()) {
+ $char = $this->consume(1);
+ if (in_array($char, $aEnd)) {
+ if ($bIncludeEnd) {
+ $out .= $char;
+ } elseif (!$consumeEnd) {
+ $this->iCurrentPosition -= $this->strlen($char);
+ }
+ return $out;
+ }
+ $out .= $char;
+ if ($comment = $this->consumeComment()) {
+ $comments[] = $comment;
+ }
+ }
+
+ if (in_array(self::EOF, $aEnd)) {
+ return $out;
+ }
+
+ $this->iCurrentPosition = $start;
+ throw new UnexpectedEOFException(
+ 'One of ("' . implode('","', $aEnd) . '")',
+ $this->peek(5),
+ 'search',
+ $this->iLineNo
+ );
+ }
+
+ /**
+ * @return string
+ */
+ private function inputLeft()
+ {
+ return $this->substr($this->iCurrentPosition, -1);
+ }
+
+ /**
+ * @param string $sString1
+ * @param string $sString2
+ * @param bool $bCaseInsensitive
+ *
+ * @return bool
+ */
+ public function streql($sString1, $sString2, $bCaseInsensitive = true)
+ {
+ if ($bCaseInsensitive) {
+ return $this->strtolower($sString1) === $this->strtolower($sString2);
+ } else {
+ return $sString1 === $sString2;
+ }
+ }
+
+ /**
+ * @param int $iAmount
+ *
+ * @return void
+ */
+ public function backtrack($iAmount)
+ {
+ $this->iCurrentPosition -= $iAmount;
+ }
+
+ /**
+ * @param string $sString
+ *
+ * @return int
+ */
+ public function strlen($sString)
+ {
+ if ($this->oParserSettings->bMultibyteSupport) {
+ return mb_strlen($sString, $this->sCharset);
+ } else {
+ return strlen($sString);
+ }
+ }
+
+ /**
+ * @param int $iStart
+ * @param int $iLength
+ *
+ * @return string
+ */
+ private function substr($iStart, $iLength)
+ {
+ if ($iLength < 0) {
+ $iLength = $this->iLength - $iStart + $iLength;
+ }
+ if ($iStart + $iLength > $this->iLength) {
+ $iLength = $this->iLength - $iStart;
+ }
+ $sResult = '';
+ while ($iLength > 0) {
+ $sResult .= $this->aText[$iStart];
+ $iStart++;
+ $iLength--;
+ }
+ return $sResult;
+ }
+
+ /**
+ * @param string $sString
+ *
+ * @return string
+ */
+ private function strtolower($sString)
+ {
+ if ($this->oParserSettings->bMultibyteSupport) {
+ return mb_strtolower($sString, $this->sCharset);
+ } else {
+ return strtolower($sString);
+ }
+ }
+
+ /**
+ * @param string $sString
+ *
+ * @return array
+ */
+ private function strsplit($sString)
+ {
+ if ($this->oParserSettings->bMultibyteSupport) {
+ if ($this->streql($this->sCharset, 'utf-8')) {
+ return preg_split('//u', $sString, -1, PREG_SPLIT_NO_EMPTY);
+ } else {
+ $iLength = mb_strlen($sString, $this->sCharset);
+ $aResult = [];
+ for ($i = 0; $i < $iLength; ++$i) {
+ $aResult[] = mb_substr($sString, $i, 1, $this->sCharset);
+ }
+ return $aResult;
+ }
+ } else {
+ if ($sString === '') {
+ return [];
+ } else {
+ return str_split($sString);
+ }
+ }
+ }
+
+ /**
+ * @param string $sString
+ * @param string $sNeedle
+ * @param int $iOffset
+ *
+ * @return int|false
+ */
+ private function strpos($sString, $sNeedle, $iOffset)
+ {
+ if ($this->oParserSettings->bMultibyteSupport) {
+ return mb_strpos($sString, $sNeedle, $iOffset, $this->sCharset);
+ } else {
+ return strpos($sString, $sNeedle, $iOffset);
+ }
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php
new file mode 100644
index 000000000..1ca668a99
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php
@@ -0,0 +1,32 @@
+iLineNo = $iLineNo;
+ if (!empty($iLineNo)) {
+ $sMessage .= " [line no: $iLineNo]";
+ }
+ parent::__construct($sMessage);
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php
new file mode 100644
index 000000000..368ec70c7
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php
@@ -0,0 +1,12 @@
+sExpected = $sExpected;
+ $this->sFound = $sFound;
+ $this->sMatchType = $sMatchType;
+ $sMessage = "Token “{$sExpected}” ({$sMatchType}) not found. Got “{$sFound}”.";
+ if ($this->sMatchType === 'search') {
+ $sMessage = "Search for “{$sExpected}” returned no results. Context: “{$sFound}”.";
+ } elseif ($this->sMatchType === 'count') {
+ $sMessage = "Next token was expected to have {$sExpected} chars. Context: “{$sFound}”.";
+ } elseif ($this->sMatchType === 'identifier') {
+ $sMessage = "Identifier expected. Got “{$sFound}”";
+ } elseif ($this->sMatchType === 'custom') {
+ $sMessage = trim("$sExpected $sFound");
+ }
+
+ parent::__construct($sMessage, $iLineNo);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/AtRule.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/AtRule.php
new file mode 100644
index 000000000..9536ff5e9
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/AtRule.php
@@ -0,0 +1,34 @@
+
+ */
+ protected $aComments;
+
+ /**
+ * @param string $mUrl
+ * @param string|null $sPrefix
+ * @param int $iLineNo
+ */
+ public function __construct($mUrl, $sPrefix = null, $iLineNo = 0)
+ {
+ $this->mUrl = $mUrl;
+ $this->sPrefix = $sPrefix;
+ $this->iLineNo = $iLineNo;
+ $this->aComments = [];
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ return '@namespace ' . ($this->sPrefix === null ? '' : $this->sPrefix . ' ')
+ . $this->mUrl->render($oOutputFormat) . ';';
+ }
+
+ /**
+ * @return string
+ */
+ public function getUrl()
+ {
+ return $this->mUrl;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getPrefix()
+ {
+ return $this->sPrefix;
+ }
+
+ /**
+ * @param string $mUrl
+ *
+ * @return void
+ */
+ public function setUrl($mUrl)
+ {
+ $this->mUrl = $mUrl;
+ }
+
+ /**
+ * @param string $sPrefix
+ *
+ * @return void
+ */
+ public function setPrefix($sPrefix)
+ {
+ $this->sPrefix = $sPrefix;
+ }
+
+ /**
+ * @return string
+ */
+ public function atRuleName()
+ {
+ return 'namespace';
+ }
+
+ /**
+ * @return array
+ */
+ public function atRuleArgs()
+ {
+ $aResult = [$this->mUrl];
+ if ($this->sPrefix) {
+ array_unshift($aResult, $this->sPrefix);
+ }
+ return $aResult;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function addComments(array $aComments)
+ {
+ $this->aComments = array_merge($this->aComments, $aComments);
+ }
+
+ /**
+ * @return array
+ */
+ public function getComments()
+ {
+ return $this->aComments;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function setComments(array $aComments)
+ {
+ $this->aComments = $aComments;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Charset.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Charset.php
new file mode 100644
index 000000000..3ee0c3d04
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Charset.php
@@ -0,0 +1,129 @@
+
+ */
+ protected $aComments;
+
+ /**
+ * @param string $sCharset
+ * @param int $iLineNo
+ */
+ public function __construct($sCharset, $iLineNo = 0)
+ {
+ $this->sCharset = $sCharset;
+ $this->iLineNo = $iLineNo;
+ $this->aComments = [];
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+
+ /**
+ * @param string $sCharset
+ *
+ * @return void
+ */
+ public function setCharset($sCharset)
+ {
+ $this->sCharset = $sCharset;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCharset()
+ {
+ return $this->sCharset;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ return "@charset {$this->sCharset->render($oOutputFormat)};";
+ }
+
+ /**
+ * @return string
+ */
+ public function atRuleName()
+ {
+ return 'charset';
+ }
+
+ /**
+ * @return string
+ */
+ public function atRuleArgs()
+ {
+ return $this->sCharset;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function addComments(array $aComments)
+ {
+ $this->aComments = array_merge($this->aComments, $aComments);
+ }
+
+ /**
+ * @return array
+ */
+ public function getComments()
+ {
+ return $this->aComments;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function setComments(array $aComments)
+ {
+ $this->aComments = $aComments;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Import.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Import.php
new file mode 100644
index 000000000..a2253016b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Import.php
@@ -0,0 +1,137 @@
+
+ */
+ protected $aComments;
+
+ /**
+ * @param URL $oLocation
+ * @param string $sMediaQuery
+ * @param int $iLineNo
+ */
+ public function __construct(URL $oLocation, $sMediaQuery, $iLineNo = 0)
+ {
+ $this->oLocation = $oLocation;
+ $this->sMediaQuery = $sMediaQuery;
+ $this->iLineNo = $iLineNo;
+ $this->aComments = [];
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+
+ /**
+ * @param URL $oLocation
+ *
+ * @return void
+ */
+ public function setLocation($oLocation)
+ {
+ $this->oLocation = $oLocation;
+ }
+
+ /**
+ * @return URL
+ */
+ public function getLocation()
+ {
+ return $this->oLocation;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ return "@import " . $this->oLocation->render($oOutputFormat)
+ . ($this->sMediaQuery === null ? '' : ' ' . $this->sMediaQuery) . ';';
+ }
+
+ /**
+ * @return string
+ */
+ public function atRuleName()
+ {
+ return 'import';
+ }
+
+ /**
+ * @return array
+ */
+ public function atRuleArgs()
+ {
+ $aResult = [$this->oLocation];
+ if ($this->sMediaQuery) {
+ array_push($aResult, $this->sMediaQuery);
+ }
+ return $aResult;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function addComments(array $aComments)
+ {
+ $this->aComments = array_merge($this->aComments, $aComments);
+ }
+
+ /**
+ * @return array
+ */
+ public function getComments()
+ {
+ return $this->aComments;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function setComments(array $aComments)
+ {
+ $this->aComments = $aComments;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php
new file mode 100644
index 000000000..14ea5ebb7
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php
@@ -0,0 +1,23 @@
+]* # any sequence of valid unescaped characters
+ (?:\\\\.)? # a single escaped character
+ (?:([\'"]).*?(?\~]+)[\w]+ # elements
+ |
+ \:{1,2}( # pseudo-elements
+ after|before|first-letter|first-line|selection
+ ))
+ /ix';
+
+ /**
+ * regexp for specificity calculations
+ *
+ * @var string
+ */
+ const SELECTOR_VALIDATION_RX = '/
+ ^(
+ (?:
+ [a-zA-Z0-9\x{00A0}-\x{FFFF}_^$|*="\'~\[\]()\-\s\.:#+>]* # any sequence of valid unescaped characters
+ (?:\\\\.)? # a single escaped character
+ (?:([\'"]).*?(?setSelector($sSelector);
+ if ($bCalculateSpecificity) {
+ $this->getSpecificity();
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getSelector()
+ {
+ return $this->sSelector;
+ }
+
+ /**
+ * @param string $sSelector
+ *
+ * @return void
+ */
+ public function setSelector($sSelector)
+ {
+ $this->sSelector = trim($sSelector);
+ $this->iSpecificity = null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->getSelector();
+ }
+
+ /**
+ * @return int
+ */
+ public function getSpecificity()
+ {
+ if ($this->iSpecificity === null) {
+ $a = 0;
+ /// @todo should exclude \# as well as "#"
+ $aMatches = null;
+ $b = substr_count($this->sSelector, '#');
+ $c = preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $this->sSelector, $aMatches);
+ $d = preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $this->sSelector, $aMatches);
+ $this->iSpecificity = ($a * 1000) + ($b * 100) + ($c * 10) + $d;
+ }
+ return $this->iSpecificity;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Renderable.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Renderable.php
new file mode 100644
index 000000000..dc1bff3c1
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Renderable.php
@@ -0,0 +1,21 @@
+
+ */
+ private $aIeHack;
+
+ /**
+ * @var int
+ */
+ protected $iLineNo;
+
+ /**
+ * @var int
+ */
+ protected $iColNo;
+
+ /**
+ * @var array
+ */
+ protected $aComments;
+
+ /**
+ * @param string $sRule
+ * @param int $iLineNo
+ * @param int $iColNo
+ */
+ public function __construct($sRule, $iLineNo = 0, $iColNo = 0)
+ {
+ $this->sRule = $sRule;
+ $this->mValue = null;
+ $this->bIsImportant = false;
+ $this->aIeHack = [];
+ $this->iLineNo = $iLineNo;
+ $this->iColNo = $iColNo;
+ $this->aComments = [];
+ }
+
+ /**
+ * @return Rule
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public static function parse(ParserState $oParserState)
+ {
+ $aComments = $oParserState->consumeWhiteSpace();
+ $oRule = new Rule(
+ $oParserState->parseIdentifier(!$oParserState->comes("--")),
+ $oParserState->currentLine(),
+ $oParserState->currentColumn()
+ );
+ $oRule->setComments($aComments);
+ $oRule->addComments($oParserState->consumeWhiteSpace());
+ $oParserState->consume(':');
+ $oValue = Value::parseValue($oParserState, self::listDelimiterForRule($oRule->getRule()));
+ $oRule->setValue($oValue);
+ if ($oParserState->getSettings()->bLenientParsing) {
+ while ($oParserState->comes('\\')) {
+ $oParserState->consume('\\');
+ $oRule->addIeHack($oParserState->consume());
+ $oParserState->consumeWhiteSpace();
+ }
+ }
+ $oParserState->consumeWhiteSpace();
+ if ($oParserState->comes('!')) {
+ $oParserState->consume('!');
+ $oParserState->consumeWhiteSpace();
+ $oParserState->consume('important');
+ $oRule->setIsImportant(true);
+ }
+ $oParserState->consumeWhiteSpace();
+ while ($oParserState->comes(';')) {
+ $oParserState->consume(';');
+ }
+ $oParserState->consumeWhiteSpace();
+
+ return $oRule;
+ }
+
+ /**
+ * @param string $sRule
+ *
+ * @return array
+ */
+ private static function listDelimiterForRule($sRule)
+ {
+ if (preg_match('/^font($|-)/', $sRule)) {
+ return [',', '/', ' '];
+ }
+ return [',', ' ', '/'];
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+
+ /**
+ * @return int
+ */
+ public function getColNo()
+ {
+ return $this->iColNo;
+ }
+
+ /**
+ * @param int $iLine
+ * @param int $iColumn
+ *
+ * @return void
+ */
+ public function setPosition($iLine, $iColumn)
+ {
+ $this->iColNo = $iColumn;
+ $this->iLineNo = $iLine;
+ }
+
+ /**
+ * @param string $sRule
+ *
+ * @return void
+ */
+ public function setRule($sRule)
+ {
+ $this->sRule = $sRule;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRule()
+ {
+ return $this->sRule;
+ }
+
+ /**
+ * @return RuleValueList|null
+ */
+ public function getValue()
+ {
+ return $this->mValue;
+ }
+
+ /**
+ * @param RuleValueList|null $mValue
+ *
+ * @return void
+ */
+ public function setValue($mValue)
+ {
+ $this->mValue = $mValue;
+ }
+
+ /**
+ * @param array> $aSpaceSeparatedValues
+ *
+ * @return RuleValueList
+ *
+ * @deprecated will be removed in version 9.0
+ * Old-Style 2-dimensional array given. Retained for (some) backwards-compatibility.
+ * Use `setValue()` instead and wrap the value inside a RuleValueList if necessary.
+ */
+ public function setValues(array $aSpaceSeparatedValues)
+ {
+ $oSpaceSeparatedList = null;
+ if (count($aSpaceSeparatedValues) > 1) {
+ $oSpaceSeparatedList = new RuleValueList(' ', $this->iLineNo);
+ }
+ foreach ($aSpaceSeparatedValues as $aCommaSeparatedValues) {
+ $oCommaSeparatedList = null;
+ if (count($aCommaSeparatedValues) > 1) {
+ $oCommaSeparatedList = new RuleValueList(',', $this->iLineNo);
+ }
+ foreach ($aCommaSeparatedValues as $mValue) {
+ if (!$oSpaceSeparatedList && !$oCommaSeparatedList) {
+ $this->mValue = $mValue;
+ return $mValue;
+ }
+ if ($oCommaSeparatedList) {
+ $oCommaSeparatedList->addListComponent($mValue);
+ } else {
+ $oSpaceSeparatedList->addListComponent($mValue);
+ }
+ }
+ if (!$oSpaceSeparatedList) {
+ $this->mValue = $oCommaSeparatedList;
+ return $oCommaSeparatedList;
+ } else {
+ $oSpaceSeparatedList->addListComponent($oCommaSeparatedList);
+ }
+ }
+ $this->mValue = $oSpaceSeparatedList;
+ return $oSpaceSeparatedList;
+ }
+
+ /**
+ * @return array>
+ *
+ * @deprecated will be removed in version 9.0
+ * Old-Style 2-dimensional array returned. Retained for (some) backwards-compatibility.
+ * Use `getValue()` instead and check for the existence of a (nested set of) ValueList object(s).
+ */
+ public function getValues()
+ {
+ if (!$this->mValue instanceof RuleValueList) {
+ return [[$this->mValue]];
+ }
+ if ($this->mValue->getListSeparator() === ',') {
+ return [$this->mValue->getListComponents()];
+ }
+ $aResult = [];
+ foreach ($this->mValue->getListComponents() as $mValue) {
+ if (!$mValue instanceof RuleValueList || $mValue->getListSeparator() !== ',') {
+ $aResult[] = [$mValue];
+ continue;
+ }
+ if ($this->mValue->getListSeparator() === ' ' || count($aResult) === 0) {
+ $aResult[] = [];
+ }
+ foreach ($mValue->getListComponents() as $mValue) {
+ $aResult[count($aResult) - 1][] = $mValue;
+ }
+ }
+ return $aResult;
+ }
+
+ /**
+ * Adds a value to the existing value. Value will be appended if a `RuleValueList` exists of the given type.
+ * Otherwise, the existing value will be wrapped by one.
+ *
+ * @param RuleValueList|array $mValue
+ * @param string $sType
+ *
+ * @return void
+ */
+ public function addValue($mValue, $sType = ' ')
+ {
+ if (!is_array($mValue)) {
+ $mValue = [$mValue];
+ }
+ if (!$this->mValue instanceof RuleValueList || $this->mValue->getListSeparator() !== $sType) {
+ $mCurrentValue = $this->mValue;
+ $this->mValue = new RuleValueList($sType, $this->iLineNo);
+ if ($mCurrentValue) {
+ $this->mValue->addListComponent($mCurrentValue);
+ }
+ }
+ foreach ($mValue as $mValueItem) {
+ $this->mValue->addListComponent($mValueItem);
+ }
+ }
+
+ /**
+ * @param int $iModifier
+ *
+ * @return void
+ */
+ public function addIeHack($iModifier)
+ {
+ $this->aIeHack[] = $iModifier;
+ }
+
+ /**
+ * @param array $aModifiers
+ *
+ * @return void
+ */
+ public function setIeHack(array $aModifiers)
+ {
+ $this->aIeHack = $aModifiers;
+ }
+
+ /**
+ * @return array
+ */
+ public function getIeHack()
+ {
+ return $this->aIeHack;
+ }
+
+ /**
+ * @param bool $bIsImportant
+ *
+ * @return void
+ */
+ public function setIsImportant($bIsImportant)
+ {
+ $this->bIsImportant = $bIsImportant;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getIsImportant()
+ {
+ return $this->bIsImportant;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $sResult = "{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}";
+ if ($this->mValue instanceof Value) { //Can also be a ValueList
+ $sResult .= $this->mValue->render($oOutputFormat);
+ } else {
+ $sResult .= $this->mValue;
+ }
+ if (!empty($this->aIeHack)) {
+ $sResult .= ' \\' . implode('\\', $this->aIeHack);
+ }
+ if ($this->bIsImportant) {
+ $sResult .= ' !important';
+ }
+ $sResult .= ';';
+ return $sResult;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function addComments(array $aComments)
+ {
+ $this->aComments = array_merge($this->aComments, $aComments);
+ }
+
+ /**
+ * @return array
+ */
+ public function getComments()
+ {
+ return $this->aComments;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function setComments(array $aComments)
+ {
+ $this->aComments = $aComments;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php
new file mode 100644
index 000000000..88bc5bd31
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php
@@ -0,0 +1,73 @@
+sType = $sType;
+ $this->sArgs = $sArgs;
+ }
+
+ /**
+ * @return string
+ */
+ public function atRuleName()
+ {
+ return $this->sType;
+ }
+
+ /**
+ * @return string
+ */
+ public function atRuleArgs()
+ {
+ return $this->sArgs;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $sArgs = $this->sArgs;
+ if ($sArgs) {
+ $sArgs = ' ' . $sArgs;
+ }
+ $sResult = "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{";
+ $sResult .= parent::render($oOutputFormat);
+ $sResult .= '}';
+ return $sResult;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php
new file mode 100644
index 000000000..c27cdd4c8
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php
@@ -0,0 +1,831 @@
+
+ */
+ private $aSelectors;
+
+ /**
+ * @param int $iLineNo
+ */
+ public function __construct($iLineNo = 0)
+ {
+ parent::__construct($iLineNo);
+ $this->aSelectors = [];
+ }
+
+ /**
+ * @param CSSList|null $oList
+ *
+ * @return DeclarationBlock|false
+ *
+ * @throws UnexpectedTokenException
+ * @throws UnexpectedEOFException
+ */
+ public static function parse(ParserState $oParserState, $oList = null)
+ {
+ $aComments = [];
+ $oResult = new DeclarationBlock($oParserState->currentLine());
+ try {
+ $aSelectorParts = [];
+ $sStringWrapperChar = false;
+ do {
+ $aSelectorParts[] = $oParserState->consume(1)
+ . $oParserState->consumeUntil(['{', '}', '\'', '"'], false, false, $aComments);
+ if (in_array($oParserState->peek(), ['\'', '"']) && substr(end($aSelectorParts), -1) != "\\") {
+ if ($sStringWrapperChar === false) {
+ $sStringWrapperChar = $oParserState->peek();
+ } elseif ($sStringWrapperChar == $oParserState->peek()) {
+ $sStringWrapperChar = false;
+ }
+ }
+ } while (!in_array($oParserState->peek(), ['{', '}']) || $sStringWrapperChar !== false);
+ $oResult->setSelectors(implode('', $aSelectorParts), $oList);
+ if ($oParserState->comes('{')) {
+ $oParserState->consume(1);
+ }
+ } catch (UnexpectedTokenException $e) {
+ if ($oParserState->getSettings()->bLenientParsing) {
+ if (!$oParserState->comes('}')) {
+ $oParserState->consumeUntil('}', false, true);
+ }
+ return false;
+ } else {
+ throw $e;
+ }
+ }
+ $oResult->setComments($aComments);
+ RuleSet::parseRuleSet($oParserState, $oResult);
+ return $oResult;
+ }
+
+ /**
+ * @param array|string $mSelector
+ * @param CSSList|null $oList
+ *
+ * @throws UnexpectedTokenException
+ */
+ public function setSelectors($mSelector, $oList = null)
+ {
+ if (is_array($mSelector)) {
+ $this->aSelectors = $mSelector;
+ } else {
+ $this->aSelectors = explode(',', $mSelector);
+ }
+ foreach ($this->aSelectors as $iKey => $mSelector) {
+ if (!($mSelector instanceof Selector)) {
+ if ($oList === null || !($oList instanceof KeyFrame)) {
+ if (!Selector::isValid($mSelector)) {
+ throw new UnexpectedTokenException(
+ "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
+ $mSelector,
+ "custom"
+ );
+ }
+ $this->aSelectors[$iKey] = new Selector($mSelector);
+ } else {
+ if (!KeyframeSelector::isValid($mSelector)) {
+ throw new UnexpectedTokenException(
+ "Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.",
+ $mSelector,
+ "custom"
+ );
+ }
+ $this->aSelectors[$iKey] = new KeyframeSelector($mSelector);
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove one of the selectors of the block.
+ *
+ * @param Selector|string $mSelector
+ *
+ * @return bool
+ */
+ public function removeSelector($mSelector)
+ {
+ if ($mSelector instanceof Selector) {
+ $mSelector = $mSelector->getSelector();
+ }
+ foreach ($this->aSelectors as $iKey => $oSelector) {
+ if ($oSelector->getSelector() === $mSelector) {
+ unset($this->aSelectors[$iKey]);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return array
+ *
+ * @deprecated will be removed in version 9.0; use `getSelectors()` instead
+ */
+ public function getSelector()
+ {
+ return $this->getSelectors();
+ }
+
+ /**
+ * @param Selector|string $mSelector
+ * @param CSSList|null $oList
+ *
+ * @return void
+ *
+ * @deprecated will be removed in version 9.0; use `setSelectors()` instead
+ */
+ public function setSelector($mSelector, $oList = null)
+ {
+ $this->setSelectors($mSelector, $oList);
+ }
+
+ /**
+ * @return array
+ */
+ public function getSelectors()
+ {
+ return $this->aSelectors;
+ }
+
+ /**
+ * Splits shorthand declarations (e.g. `margin` or `font`) into their constituent parts.
+ *
+ * @return void
+ */
+ public function expandShorthands()
+ {
+ // border must be expanded before dimensions
+ $this->expandBorderShorthand();
+ $this->expandDimensionsShorthand();
+ $this->expandFontShorthand();
+ $this->expandBackgroundShorthand();
+ $this->expandListStyleShorthand();
+ }
+
+ /**
+ * Creates shorthand declarations (e.g. `margin` or `font`) whenever possible.
+ *
+ * @return void
+ */
+ public function createShorthands()
+ {
+ $this->createBackgroundShorthand();
+ $this->createDimensionsShorthand();
+ // border must be shortened after dimensions
+ $this->createBorderShorthand();
+ $this->createFontShorthand();
+ $this->createListStyleShorthand();
+ }
+
+ /**
+ * Splits shorthand border declarations (e.g. `border: 1px red;`).
+ *
+ * Additional splitting happens in expandDimensionsShorthand.
+ *
+ * Multiple borders are not yet supported as of 3.
+ *
+ * @return void
+ */
+ public function expandBorderShorthand()
+ {
+ $aBorderRules = [
+ 'border',
+ 'border-left',
+ 'border-right',
+ 'border-top',
+ 'border-bottom',
+ ];
+ $aBorderSizes = [
+ 'thin',
+ 'medium',
+ 'thick',
+ ];
+ $aRules = $this->getRulesAssoc();
+ foreach ($aBorderRules as $sBorderRule) {
+ if (!isset($aRules[$sBorderRule])) {
+ continue;
+ }
+ $oRule = $aRules[$sBorderRule];
+ $mRuleValue = $oRule->getValue();
+ $aValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aValues[] = $mRuleValue;
+ } else {
+ $aValues = $mRuleValue->getListComponents();
+ }
+ foreach ($aValues as $mValue) {
+ if ($mValue instanceof Value) {
+ $mNewValue = clone $mValue;
+ } else {
+ $mNewValue = $mValue;
+ }
+ if ($mValue instanceof Size) {
+ $sNewRuleName = $sBorderRule . "-width";
+ } elseif ($mValue instanceof Color) {
+ $sNewRuleName = $sBorderRule . "-color";
+ } else {
+ if (in_array($mValue, $aBorderSizes)) {
+ $sNewRuleName = $sBorderRule . "-width";
+ } else {
+ $sNewRuleName = $sBorderRule . "-style";
+ }
+ }
+ $oNewRule = new Rule($sNewRuleName, $oRule->getLineNo(), $oRule->getColNo());
+ $oNewRule->setIsImportant($oRule->getIsImportant());
+ $oNewRule->addValue([$mNewValue]);
+ $this->addRule($oNewRule);
+ }
+ $this->removeRule($sBorderRule);
+ }
+ }
+
+ /**
+ * Splits shorthand dimensional declarations (e.g. `margin: 0px auto;`)
+ * into their constituent parts.
+ *
+ * Handles `margin`, `padding`, `border-color`, `border-style` and `border-width`.
+ *
+ * @return void
+ */
+ public function expandDimensionsShorthand()
+ {
+ $aExpansions = [
+ 'margin' => 'margin-%s',
+ 'padding' => 'padding-%s',
+ 'border-color' => 'border-%s-color',
+ 'border-style' => 'border-%s-style',
+ 'border-width' => 'border-%s-width',
+ ];
+ $aRules = $this->getRulesAssoc();
+ foreach ($aExpansions as $sProperty => $sExpanded) {
+ if (!isset($aRules[$sProperty])) {
+ continue;
+ }
+ $oRule = $aRules[$sProperty];
+ $mRuleValue = $oRule->getValue();
+ $aValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aValues[] = $mRuleValue;
+ } else {
+ $aValues = $mRuleValue->getListComponents();
+ }
+ $top = $right = $bottom = $left = null;
+ switch (count($aValues)) {
+ case 1:
+ $top = $right = $bottom = $left = $aValues[0];
+ break;
+ case 2:
+ $top = $bottom = $aValues[0];
+ $left = $right = $aValues[1];
+ break;
+ case 3:
+ $top = $aValues[0];
+ $left = $right = $aValues[1];
+ $bottom = $aValues[2];
+ break;
+ case 4:
+ $top = $aValues[0];
+ $right = $aValues[1];
+ $bottom = $aValues[2];
+ $left = $aValues[3];
+ break;
+ }
+ foreach (['top', 'right', 'bottom', 'left'] as $sPosition) {
+ $oNewRule = new Rule(sprintf($sExpanded, $sPosition), $oRule->getLineNo(), $oRule->getColNo());
+ $oNewRule->setIsImportant($oRule->getIsImportant());
+ $oNewRule->addValue(${$sPosition});
+ $this->addRule($oNewRule);
+ }
+ $this->removeRule($sProperty);
+ }
+ }
+
+ /**
+ * Converts shorthand font declarations
+ * (e.g. `font: 300 italic 11px/14px verdana, helvetica, sans-serif;`)
+ * into their constituent parts.
+ *
+ * @return void
+ */
+ public function expandFontShorthand()
+ {
+ $aRules = $this->getRulesAssoc();
+ if (!isset($aRules['font'])) {
+ return;
+ }
+ $oRule = $aRules['font'];
+ // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand
+ $aFontProperties = [
+ 'font-style' => 'normal',
+ 'font-variant' => 'normal',
+ 'font-weight' => 'normal',
+ 'font-size' => 'normal',
+ 'line-height' => 'normal',
+ ];
+ $mRuleValue = $oRule->getValue();
+ $aValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aValues[] = $mRuleValue;
+ } else {
+ $aValues = $mRuleValue->getListComponents();
+ }
+ foreach ($aValues as $mValue) {
+ if (!$mValue instanceof Value) {
+ $mValue = mb_strtolower($mValue);
+ }
+ if (in_array($mValue, ['normal', 'inherit'])) {
+ foreach (['font-style', 'font-weight', 'font-variant'] as $sProperty) {
+ if (!isset($aFontProperties[$sProperty])) {
+ $aFontProperties[$sProperty] = $mValue;
+ }
+ }
+ } elseif (in_array($mValue, ['italic', 'oblique'])) {
+ $aFontProperties['font-style'] = $mValue;
+ } elseif ($mValue == 'small-caps') {
+ $aFontProperties['font-variant'] = $mValue;
+ } elseif (
+ in_array($mValue, ['bold', 'bolder', 'lighter'])
+ || ($mValue instanceof Size
+ && in_array($mValue->getSize(), range(100, 900, 100)))
+ ) {
+ $aFontProperties['font-weight'] = $mValue;
+ } elseif ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') {
+ list($oSize, $oHeight) = $mValue->getListComponents();
+ $aFontProperties['font-size'] = $oSize;
+ $aFontProperties['line-height'] = $oHeight;
+ } elseif ($mValue instanceof Size && $mValue->getUnit() !== null) {
+ $aFontProperties['font-size'] = $mValue;
+ } else {
+ $aFontProperties['font-family'] = $mValue;
+ }
+ }
+ foreach ($aFontProperties as $sProperty => $mValue) {
+ $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
+ $oNewRule->addValue($mValue);
+ $oNewRule->setIsImportant($oRule->getIsImportant());
+ $this->addRule($oNewRule);
+ }
+ $this->removeRule('font');
+ }
+
+ /**
+ * Converts shorthand background declarations
+ * (e.g. `background: url("chess.png") gray 50% repeat fixed;`)
+ * into their constituent parts.
+ *
+ * @see http://www.w3.org/TR/21/colors.html#propdef-background
+ *
+ * @return void
+ */
+ public function expandBackgroundShorthand()
+ {
+ $aRules = $this->getRulesAssoc();
+ if (!isset($aRules['background'])) {
+ return;
+ }
+ $oRule = $aRules['background'];
+ $aBgProperties = [
+ 'background-color' => ['transparent'],
+ 'background-image' => ['none'],
+ 'background-repeat' => ['repeat'],
+ 'background-attachment' => ['scroll'],
+ 'background-position' => [
+ new Size(0, '%', null, false, $this->iLineNo),
+ new Size(0, '%', null, false, $this->iLineNo),
+ ],
+ ];
+ $mRuleValue = $oRule->getValue();
+ $aValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aValues[] = $mRuleValue;
+ } else {
+ $aValues = $mRuleValue->getListComponents();
+ }
+ if (count($aValues) == 1 && $aValues[0] == 'inherit') {
+ foreach ($aBgProperties as $sProperty => $mValue) {
+ $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
+ $oNewRule->addValue('inherit');
+ $oNewRule->setIsImportant($oRule->getIsImportant());
+ $this->addRule($oNewRule);
+ }
+ $this->removeRule('background');
+ return;
+ }
+ $iNumBgPos = 0;
+ foreach ($aValues as $mValue) {
+ if (!$mValue instanceof Value) {
+ $mValue = mb_strtolower($mValue);
+ }
+ if ($mValue instanceof URL) {
+ $aBgProperties['background-image'] = $mValue;
+ } elseif ($mValue instanceof Color) {
+ $aBgProperties['background-color'] = $mValue;
+ } elseif (in_array($mValue, ['scroll', 'fixed'])) {
+ $aBgProperties['background-attachment'] = $mValue;
+ } elseif (in_array($mValue, ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'])) {
+ $aBgProperties['background-repeat'] = $mValue;
+ } elseif (
+ in_array($mValue, ['left', 'center', 'right', 'top', 'bottom'])
+ || $mValue instanceof Size
+ ) {
+ if ($iNumBgPos == 0) {
+ $aBgProperties['background-position'][0] = $mValue;
+ $aBgProperties['background-position'][1] = 'center';
+ } else {
+ $aBgProperties['background-position'][$iNumBgPos] = $mValue;
+ }
+ $iNumBgPos++;
+ }
+ }
+ foreach ($aBgProperties as $sProperty => $mValue) {
+ $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
+ $oNewRule->setIsImportant($oRule->getIsImportant());
+ $oNewRule->addValue($mValue);
+ $this->addRule($oNewRule);
+ }
+ $this->removeRule('background');
+ }
+
+ /**
+ * @return void
+ */
+ public function expandListStyleShorthand()
+ {
+ $aListProperties = [
+ 'list-style-type' => 'disc',
+ 'list-style-position' => 'outside',
+ 'list-style-image' => 'none',
+ ];
+ $aListStyleTypes = [
+ 'none',
+ 'disc',
+ 'circle',
+ 'square',
+ 'decimal-leading-zero',
+ 'decimal',
+ 'lower-roman',
+ 'upper-roman',
+ 'lower-greek',
+ 'lower-alpha',
+ 'lower-latin',
+ 'upper-alpha',
+ 'upper-latin',
+ 'hebrew',
+ 'armenian',
+ 'georgian',
+ 'cjk-ideographic',
+ 'hiragana',
+ 'hira-gana-iroha',
+ 'katakana-iroha',
+ 'katakana',
+ ];
+ $aListStylePositions = [
+ 'inside',
+ 'outside',
+ ];
+ $aRules = $this->getRulesAssoc();
+ if (!isset($aRules['list-style'])) {
+ return;
+ }
+ $oRule = $aRules['list-style'];
+ $mRuleValue = $oRule->getValue();
+ $aValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aValues[] = $mRuleValue;
+ } else {
+ $aValues = $mRuleValue->getListComponents();
+ }
+ if (count($aValues) == 1 && $aValues[0] == 'inherit') {
+ foreach ($aListProperties as $sProperty => $mValue) {
+ $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
+ $oNewRule->addValue('inherit');
+ $oNewRule->setIsImportant($oRule->getIsImportant());
+ $this->addRule($oNewRule);
+ }
+ $this->removeRule('list-style');
+ return;
+ }
+ foreach ($aValues as $mValue) {
+ if (!$mValue instanceof Value) {
+ $mValue = mb_strtolower($mValue);
+ }
+ if ($mValue instanceof Url) {
+ $aListProperties['list-style-image'] = $mValue;
+ } elseif (in_array($mValue, $aListStyleTypes)) {
+ $aListProperties['list-style-types'] = $mValue;
+ } elseif (in_array($mValue, $aListStylePositions)) {
+ $aListProperties['list-style-position'] = $mValue;
+ }
+ }
+ foreach ($aListProperties as $sProperty => $mValue) {
+ $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
+ $oNewRule->setIsImportant($oRule->getIsImportant());
+ $oNewRule->addValue($mValue);
+ $this->addRule($oNewRule);
+ }
+ $this->removeRule('list-style');
+ }
+
+ /**
+ * @param array $aProperties
+ * @param string $sShorthand
+ *
+ * @return void
+ */
+ public function createShorthandProperties(array $aProperties, $sShorthand)
+ {
+ $aRules = $this->getRulesAssoc();
+ $aNewValues = [];
+ foreach ($aProperties as $sProperty) {
+ if (!isset($aRules[$sProperty])) {
+ continue;
+ }
+ $oRule = $aRules[$sProperty];
+ if (!$oRule->getIsImportant()) {
+ $mRuleValue = $oRule->getValue();
+ $aValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aValues[] = $mRuleValue;
+ } else {
+ $aValues = $mRuleValue->getListComponents();
+ }
+ foreach ($aValues as $mValue) {
+ $aNewValues[] = $mValue;
+ }
+ $this->removeRule($sProperty);
+ }
+ }
+ if (count($aNewValues)) {
+ $oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo());
+ foreach ($aNewValues as $mValue) {
+ $oNewRule->addValue($mValue);
+ }
+ $this->addRule($oNewRule);
+ }
+ }
+
+ /**
+ * @return void
+ */
+ public function createBackgroundShorthand()
+ {
+ $aProperties = [
+ 'background-color',
+ 'background-image',
+ 'background-repeat',
+ 'background-position',
+ 'background-attachment',
+ ];
+ $this->createShorthandProperties($aProperties, 'background');
+ }
+
+ /**
+ * @return void
+ */
+ public function createListStyleShorthand()
+ {
+ $aProperties = [
+ 'list-style-type',
+ 'list-style-position',
+ 'list-style-image',
+ ];
+ $this->createShorthandProperties($aProperties, 'list-style');
+ }
+
+ /**
+ * Combines `border-color`, `border-style` and `border-width` into `border`.
+ *
+ * Should be run after `create_dimensions_shorthand`!
+ *
+ * @return void
+ */
+ public function createBorderShorthand()
+ {
+ $aProperties = [
+ 'border-width',
+ 'border-style',
+ 'border-color',
+ ];
+ $this->createShorthandProperties($aProperties, 'border');
+ }
+
+ /**
+ * Looks for long format CSS dimensional properties
+ * (margin, padding, border-color, border-style and border-width)
+ * and converts them into shorthand CSS properties.
+ *
+ * @return void
+ */
+ public function createDimensionsShorthand()
+ {
+ $aPositions = ['top', 'right', 'bottom', 'left'];
+ $aExpansions = [
+ 'margin' => 'margin-%s',
+ 'padding' => 'padding-%s',
+ 'border-color' => 'border-%s-color',
+ 'border-style' => 'border-%s-style',
+ 'border-width' => 'border-%s-width',
+ ];
+ $aRules = $this->getRulesAssoc();
+ foreach ($aExpansions as $sProperty => $sExpanded) {
+ $aFoldable = [];
+ foreach ($aRules as $sRuleName => $oRule) {
+ foreach ($aPositions as $sPosition) {
+ if ($sRuleName == sprintf($sExpanded, $sPosition)) {
+ $aFoldable[$sRuleName] = $oRule;
+ }
+ }
+ }
+ // All four dimensions must be present
+ if (count($aFoldable) == 4) {
+ $aValues = [];
+ foreach ($aPositions as $sPosition) {
+ $oRule = $aRules[sprintf($sExpanded, $sPosition)];
+ $mRuleValue = $oRule->getValue();
+ $aRuleValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aRuleValues[] = $mRuleValue;
+ } else {
+ $aRuleValues = $mRuleValue->getListComponents();
+ }
+ $aValues[$sPosition] = $aRuleValues;
+ }
+ $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
+ if ((string)$aValues['left'][0] == (string)$aValues['right'][0]) {
+ if ((string)$aValues['top'][0] == (string)$aValues['bottom'][0]) {
+ if ((string)$aValues['top'][0] == (string)$aValues['left'][0]) {
+ // All 4 sides are equal
+ $oNewRule->addValue($aValues['top']);
+ } else {
+ // Top and bottom are equal, left and right are equal
+ $oNewRule->addValue($aValues['top']);
+ $oNewRule->addValue($aValues['left']);
+ }
+ } else {
+ // Only left and right are equal
+ $oNewRule->addValue($aValues['top']);
+ $oNewRule->addValue($aValues['left']);
+ $oNewRule->addValue($aValues['bottom']);
+ }
+ } else {
+ // No sides are equal
+ $oNewRule->addValue($aValues['top']);
+ $oNewRule->addValue($aValues['left']);
+ $oNewRule->addValue($aValues['bottom']);
+ $oNewRule->addValue($aValues['right']);
+ }
+ $this->addRule($oNewRule);
+ foreach ($aPositions as $sPosition) {
+ $this->removeRule(sprintf($sExpanded, $sPosition));
+ }
+ }
+ }
+ }
+
+ /**
+ * Looks for long format CSS font properties (e.g. `font-weight`) and
+ * tries to convert them into a shorthand CSS `font` property.
+ *
+ * At least `font-size` AND `font-family` must be present in order to create a shorthand declaration.
+ *
+ * @return void
+ */
+ public function createFontShorthand()
+ {
+ $aFontProperties = [
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'font-size',
+ 'line-height',
+ 'font-family',
+ ];
+ $aRules = $this->getRulesAssoc();
+ if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) {
+ return;
+ }
+ $oOldRule = isset($aRules['font-size']) ? $aRules['font-size'] : $aRules['font-family'];
+ $oNewRule = new Rule('font', $oOldRule->getLineNo(), $oOldRule->getColNo());
+ unset($oOldRule);
+ foreach (['font-style', 'font-variant', 'font-weight'] as $sProperty) {
+ if (isset($aRules[$sProperty])) {
+ $oRule = $aRules[$sProperty];
+ $mRuleValue = $oRule->getValue();
+ $aValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aValues[] = $mRuleValue;
+ } else {
+ $aValues = $mRuleValue->getListComponents();
+ }
+ if ($aValues[0] !== 'normal') {
+ $oNewRule->addValue($aValues[0]);
+ }
+ }
+ }
+ // Get the font-size value
+ $oRule = $aRules['font-size'];
+ $mRuleValue = $oRule->getValue();
+ $aFSValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aFSValues[] = $mRuleValue;
+ } else {
+ $aFSValues = $mRuleValue->getListComponents();
+ }
+ // But wait to know if we have line-height to add it
+ if (isset($aRules['line-height'])) {
+ $oRule = $aRules['line-height'];
+ $mRuleValue = $oRule->getValue();
+ $aLHValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aLHValues[] = $mRuleValue;
+ } else {
+ $aLHValues = $mRuleValue->getListComponents();
+ }
+ if ($aLHValues[0] !== 'normal') {
+ $val = new RuleValueList('/', $this->iLineNo);
+ $val->addListComponent($aFSValues[0]);
+ $val->addListComponent($aLHValues[0]);
+ $oNewRule->addValue($val);
+ }
+ } else {
+ $oNewRule->addValue($aFSValues[0]);
+ }
+ $oRule = $aRules['font-family'];
+ $mRuleValue = $oRule->getValue();
+ $aFFValues = [];
+ if (!$mRuleValue instanceof RuleValueList) {
+ $aFFValues[] = $mRuleValue;
+ } else {
+ $aFFValues = $mRuleValue->getListComponents();
+ }
+ $oFFValue = new RuleValueList(',', $this->iLineNo);
+ $oFFValue->setListComponents($aFFValues);
+ $oNewRule->addValue($oFFValue);
+
+ $this->addRule($oNewRule);
+ foreach ($aFontProperties as $sProperty) {
+ $this->removeRule($sProperty);
+ }
+ }
+
+ /**
+ * @return string
+ *
+ * @throws OutputException
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ *
+ * @throws OutputException
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ if (count($this->aSelectors) === 0) {
+ // If all the selectors have been removed, this declaration block becomes invalid
+ throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo);
+ }
+ $sResult = $oOutputFormat->sBeforeDeclarationBlock;
+ $sResult .= $oOutputFormat->implode(
+ $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(),
+ $this->aSelectors
+ );
+ $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors;
+ $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{';
+ $sResult .= parent::render($oOutputFormat);
+ $sResult .= '}';
+ $sResult .= $oOutputFormat->sAfterDeclarationBlock;
+ return $sResult;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php
new file mode 100644
index 000000000..9404bb0bd
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php
@@ -0,0 +1,326 @@
+
+ */
+ private $aRules;
+
+ /**
+ * @var int
+ */
+ protected $iLineNo;
+
+ /**
+ * @var array
+ */
+ protected $aComments;
+
+ /**
+ * @param int $iLineNo
+ */
+ public function __construct($iLineNo = 0)
+ {
+ $this->aRules = [];
+ $this->iLineNo = $iLineNo;
+ $this->aComments = [];
+ }
+
+ /**
+ * @return void
+ *
+ * @throws UnexpectedTokenException
+ * @throws UnexpectedEOFException
+ */
+ public static function parseRuleSet(ParserState $oParserState, RuleSet $oRuleSet)
+ {
+ while ($oParserState->comes(';')) {
+ $oParserState->consume(';');
+ }
+ while (!$oParserState->comes('}')) {
+ $oRule = null;
+ if ($oParserState->getSettings()->bLenientParsing) {
+ try {
+ $oRule = Rule::parse($oParserState);
+ } catch (UnexpectedTokenException $e) {
+ try {
+ $sConsume = $oParserState->consumeUntil(["\n", ";", '}'], true);
+ // We need to “unfind” the matches to the end of the ruleSet as this will be matched later
+ if ($oParserState->streql(substr($sConsume, -1), '}')) {
+ $oParserState->backtrack(1);
+ } else {
+ while ($oParserState->comes(';')) {
+ $oParserState->consume(';');
+ }
+ }
+ } catch (UnexpectedTokenException $e) {
+ // We’ve reached the end of the document. Just close the RuleSet.
+ return;
+ }
+ }
+ } else {
+ $oRule = Rule::parse($oParserState);
+ }
+ if ($oRule) {
+ $oRuleSet->addRule($oRule);
+ }
+ }
+ $oParserState->consume('}');
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+
+ /**
+ * @param Rule|null $oSibling
+ *
+ * @return void
+ */
+ public function addRule(Rule $oRule, Rule $oSibling = null)
+ {
+ $sRule = $oRule->getRule();
+ if (!isset($this->aRules[$sRule])) {
+ $this->aRules[$sRule] = [];
+ }
+
+ $iPosition = count($this->aRules[$sRule]);
+
+ if ($oSibling !== null) {
+ $iSiblingPos = array_search($oSibling, $this->aRules[$sRule], true);
+ if ($iSiblingPos !== false) {
+ $iPosition = $iSiblingPos;
+ $oRule->setPosition($oSibling->getLineNo(), $oSibling->getColNo() - 1);
+ }
+ }
+ if ($oRule->getLineNo() === 0 && $oRule->getColNo() === 0) {
+ //this node is added manually, give it the next best line
+ $rules = $this->getRules();
+ $pos = count($rules);
+ if ($pos > 0) {
+ $last = $rules[$pos - 1];
+ $oRule->setPosition($last->getLineNo() + 1, 0);
+ }
+ }
+
+ array_splice($this->aRules[$sRule], $iPosition, 0, [$oRule]);
+ }
+
+ /**
+ * Returns all rules matching the given rule name
+ *
+ * @example $oRuleSet->getRules('font') // returns array(0 => $oRule, …) or array().
+ *
+ * @example $oRuleSet->getRules('font-')
+ * //returns an array of all rules either beginning with font- or matching font.
+ *
+ * @param Rule|string|null $mRule
+ * Pattern to search for. If null, returns all rules.
+ * If the pattern ends with a dash, all rules starting with the pattern are returned
+ * as well as one matching the pattern with the dash excluded.
+ * Passing a Rule behaves like calling `getRules($mRule->getRule())`.
+ *
+ * @return array
+ */
+ public function getRules($mRule = null)
+ {
+ if ($mRule instanceof Rule) {
+ $mRule = $mRule->getRule();
+ }
+ /** @var array $aResult */
+ $aResult = [];
+ foreach ($this->aRules as $sName => $aRules) {
+ // Either no search rule is given or the search rule matches the found rule exactly
+ // or the search rule ends in “-” and the found rule starts with the search rule.
+ if (
+ !$mRule || $sName === $mRule
+ || (
+ strrpos($mRule, '-') === strlen($mRule) - strlen('-')
+ && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1))
+ )
+ ) {
+ $aResult = array_merge($aResult, $aRules);
+ }
+ }
+ usort($aResult, function (Rule $first, Rule $second) {
+ if ($first->getLineNo() === $second->getLineNo()) {
+ return $first->getColNo() - $second->getColNo();
+ }
+ return $first->getLineNo() - $second->getLineNo();
+ });
+ return $aResult;
+ }
+
+ /**
+ * Overrides all the rules of this set.
+ *
+ * @param array $aRules The rules to override with.
+ *
+ * @return void
+ */
+ public function setRules(array $aRules)
+ {
+ $this->aRules = [];
+ foreach ($aRules as $rule) {
+ $this->addRule($rule);
+ }
+ }
+
+ /**
+ * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name
+ * as keys. This method exists mainly for backwards-compatibility and is really only partially useful.
+ *
+ * Note: This method loses some information: Calling this (with an argument of `background-`) on a declaration block
+ * like `{ background-color: green; background-color; rgba(0, 127, 0, 0.7); }` will only yield an associative array
+ * containing the rgba-valued rule while `getRules()` would yield an indexed array containing both.
+ *
+ * @param Rule|string|null $mRule $mRule
+ * Pattern to search for. If null, returns all rules. If the pattern ends with a dash,
+ * all rules starting with the pattern are returned as well as one matching the pattern with the dash
+ * excluded. Passing a Rule behaves like calling `getRules($mRule->getRule())`.
+ *
+ * @return array
+ */
+ public function getRulesAssoc($mRule = null)
+ {
+ /** @var array $aResult */
+ $aResult = [];
+ foreach ($this->getRules($mRule) as $oRule) {
+ $aResult[$oRule->getRule()] = $oRule;
+ }
+ return $aResult;
+ }
+
+ /**
+ * Removes a rule from this RuleSet. This accepts all the possible values that `getRules()` accepts.
+ *
+ * If given a Rule, it will only remove this particular rule (by identity).
+ * If given a name, it will remove all rules by that name.
+ *
+ * Note: this is different from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would
+ * remove all rules with the same name. To get the old behaviour, use `removeRule($oRule->getRule())`.
+ *
+ * @param Rule|string|null $mRule
+ * pattern to remove. If $mRule is null, all rules are removed. If the pattern ends in a dash,
+ * all rules starting with the pattern are removed as well as one matching the pattern with the dash
+ * excluded. Passing a Rule behaves matches by identity.
+ *
+ * @return void
+ */
+ public function removeRule($mRule)
+ {
+ if ($mRule instanceof Rule) {
+ $sRule = $mRule->getRule();
+ if (!isset($this->aRules[$sRule])) {
+ return;
+ }
+ foreach ($this->aRules[$sRule] as $iKey => $oRule) {
+ if ($oRule === $mRule) {
+ unset($this->aRules[$sRule][$iKey]);
+ }
+ }
+ } else {
+ foreach ($this->aRules as $sName => $aRules) {
+ // Either no search rule is given or the search rule matches the found rule exactly
+ // or the search rule ends in “-” and the found rule starts with the search rule or equals it
+ // (without the trailing dash).
+ if (
+ !$mRule || $sName === $mRule
+ || (strrpos($mRule, '-') === strlen($mRule) - strlen('-')
+ && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1)))
+ ) {
+ unset($this->aRules[$sName]);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $sResult = '';
+ $bIsFirst = true;
+ foreach ($this->aRules as $aRules) {
+ foreach ($aRules as $oRule) {
+ $sRendered = $oOutputFormat->safely(function () use ($oRule, $oOutputFormat) {
+ return $oRule->render($oOutputFormat->nextLevel());
+ });
+ if ($sRendered === null) {
+ continue;
+ }
+ if ($bIsFirst) {
+ $bIsFirst = false;
+ $sResult .= $oOutputFormat->nextLevel()->spaceBeforeRules();
+ } else {
+ $sResult .= $oOutputFormat->nextLevel()->spaceBetweenRules();
+ }
+ $sResult .= $sRendered;
+ }
+ }
+
+ if (!$bIsFirst) {
+ // Had some output
+ $sResult .= $oOutputFormat->spaceAfterRules();
+ }
+
+ return $oOutputFormat->removeLastSemicolon($sResult);
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function addComments(array $aComments)
+ {
+ $this->aComments = array_merge($this->aComments, $aComments);
+ }
+
+ /**
+ * @return array
+ */
+ public function getComments()
+ {
+ return $this->aComments;
+ }
+
+ /**
+ * @param array $aComments
+ *
+ * @return void
+ */
+ public function setComments(array $aComments)
+ {
+ $this->aComments = $aComments;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Settings.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Settings.php
new file mode 100644
index 000000000..7b8580962
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Settings.php
@@ -0,0 +1,89 @@
+bMultibyteSupport = extension_loaded('mbstring');
+ }
+
+ /**
+ * @return self new instance
+ */
+ public static function create()
+ {
+ return new Settings();
+ }
+
+ /**
+ * @param bool $bMultibyteSupport
+ *
+ * @return self fluent interface
+ */
+ public function withMultibyteSupport($bMultibyteSupport = true)
+ {
+ $this->bMultibyteSupport = $bMultibyteSupport;
+ return $this;
+ }
+
+ /**
+ * @param string $sDefaultCharset
+ *
+ * @return self fluent interface
+ */
+ public function withDefaultCharset($sDefaultCharset)
+ {
+ $this->sDefaultCharset = $sDefaultCharset;
+ return $this;
+ }
+
+ /**
+ * @param bool $bLenientParsing
+ *
+ * @return self fluent interface
+ */
+ public function withLenientParsing($bLenientParsing = true)
+ {
+ $this->bLenientParsing = $bLenientParsing;
+ return $this;
+ }
+
+ /**
+ * @return self fluent interface
+ */
+ public function beStrict()
+ {
+ return $this->withLenientParsing(false);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php
new file mode 100644
index 000000000..e6b8c1189
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php
@@ -0,0 +1,73 @@
+ $aArguments
+ * @param string $sSeparator
+ * @param int $iLineNo
+ */
+ public function __construct($sName, $aArguments, $sSeparator = ',', $iLineNo = 0)
+ {
+ if ($aArguments instanceof RuleValueList) {
+ $sSeparator = $aArguments->getListSeparator();
+ $aArguments = $aArguments->getListComponents();
+ }
+ $this->sName = $sName;
+ $this->iLineNo = $iLineNo;
+ parent::__construct($aArguments, $sSeparator, $iLineNo);
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->sName;
+ }
+
+ /**
+ * @param string $sName
+ *
+ * @return void
+ */
+ public function setName($sName)
+ {
+ $this->sName = $sName;
+ }
+
+ /**
+ * @return array
+ */
+ public function getArguments()
+ {
+ return $this->aComponents;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $aArguments = parent::render($oOutputFormat);
+ return "{$this->sName}({$aArguments})";
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSString.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSString.php
new file mode 100644
index 000000000..9fafedd7a
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSString.php
@@ -0,0 +1,105 @@
+sString = $sString;
+ parent::__construct($iLineNo);
+ }
+
+ /**
+ * @return CSSString
+ *
+ * @throws SourceException
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public static function parse(ParserState $oParserState)
+ {
+ $sBegin = $oParserState->peek();
+ $sQuote = null;
+ if ($sBegin === "'") {
+ $sQuote = "'";
+ } elseif ($sBegin === '"') {
+ $sQuote = '"';
+ }
+ if ($sQuote !== null) {
+ $oParserState->consume($sQuote);
+ }
+ $sResult = "";
+ $sContent = null;
+ if ($sQuote === null) {
+ // Unquoted strings end in whitespace or with braces, brackets, parentheses
+ while (!preg_match('/[\\s{}()<>\\[\\]]/isu', $oParserState->peek())) {
+ $sResult .= $oParserState->parseCharacter(false);
+ }
+ } else {
+ while (!$oParserState->comes($sQuote)) {
+ $sContent = $oParserState->parseCharacter(false);
+ if ($sContent === null) {
+ throw new SourceException(
+ "Non-well-formed quoted string {$oParserState->peek(3)}",
+ $oParserState->currentLine()
+ );
+ }
+ $sResult .= $sContent;
+ }
+ $oParserState->consume($sQuote);
+ }
+ return new CSSString($sResult, $oParserState->currentLine());
+ }
+
+ /**
+ * @param string $sString
+ *
+ * @return void
+ */
+ public function setString($sString)
+ {
+ $this->sString = $sString;
+ }
+
+ /**
+ * @return string
+ */
+ public function getString()
+ {
+ return $this->sString;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $sString = addslashes($this->sString);
+ $sString = str_replace("\n", '\A', $sString);
+ return $oOutputFormat->getStringQuotingType() . $sString . $oOutputFormat->getStringQuotingType();
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php
new file mode 100644
index 000000000..5c92e0c08
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php
@@ -0,0 +1,89 @@
+consumeUntil('(', false, true));
+ $oCalcList = new CalcRuleValueList($oParserState->currentLine());
+ $oList = new RuleValueList(',', $oParserState->currentLine());
+ $iNestingLevel = 0;
+ $iLastComponentType = null;
+ while (!$oParserState->comes(')') || $iNestingLevel > 0) {
+ $oParserState->consumeWhiteSpace();
+ if ($oParserState->comes('(')) {
+ $iNestingLevel++;
+ $oCalcList->addListComponent($oParserState->consume(1));
+ $oParserState->consumeWhiteSpace();
+ continue;
+ } elseif ($oParserState->comes(')')) {
+ $iNestingLevel--;
+ $oCalcList->addListComponent($oParserState->consume(1));
+ $oParserState->consumeWhiteSpace();
+ continue;
+ }
+ if ($iLastComponentType != CalcFunction::T_OPERAND) {
+ $oVal = Value::parsePrimitiveValue($oParserState);
+ $oCalcList->addListComponent($oVal);
+ $iLastComponentType = CalcFunction::T_OPERAND;
+ } else {
+ if (in_array($oParserState->peek(), $aOperators)) {
+ if (($oParserState->comes('-') || $oParserState->comes('+'))) {
+ if (
+ $oParserState->peek(1, -1) != ' '
+ || !($oParserState->comes('- ')
+ || $oParserState->comes('+ '))
+ ) {
+ throw new UnexpectedTokenException(
+ " {$oParserState->peek()} ",
+ $oParserState->peek(1, -1) . $oParserState->peek(2),
+ 'literal',
+ $oParserState->currentLine()
+ );
+ }
+ }
+ $oCalcList->addListComponent($oParserState->consume(1));
+ $iLastComponentType = CalcFunction::T_OPERATOR;
+ } else {
+ throw new UnexpectedTokenException(
+ sprintf(
+ 'Next token was expected to be an operand of type %s. Instead "%s" was found.',
+ implode(', ', $aOperators),
+ $oVal
+ ),
+ '',
+ 'custom',
+ $oParserState->currentLine()
+ );
+ }
+ }
+ $oParserState->consumeWhiteSpace();
+ }
+ $oList->addListComponent($oCalcList);
+ $oParserState->consume(')');
+ return new CalcFunction($sFunction, $oList, ',', $oParserState->currentLine());
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php
new file mode 100644
index 000000000..7dbd26a1b
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php
@@ -0,0 +1,24 @@
+implode(' ', $this->aComponents);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Color.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Color.php
new file mode 100644
index 000000000..8dc52960c
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Color.php
@@ -0,0 +1,166 @@
+ $aColor
+ * @param int $iLineNo
+ */
+ public function __construct(array $aColor, $iLineNo = 0)
+ {
+ parent::__construct(implode('', array_keys($aColor)), $aColor, ',', $iLineNo);
+ }
+
+ /**
+ * @return Color|CSSFunction
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public static function parse(ParserState $oParserState)
+ {
+ $aColor = [];
+ if ($oParserState->comes('#')) {
+ $oParserState->consume('#');
+ $sValue = $oParserState->parseIdentifier(false);
+ if ($oParserState->strlen($sValue) === 3) {
+ $sValue = $sValue[0] . $sValue[0] . $sValue[1] . $sValue[1] . $sValue[2] . $sValue[2];
+ } elseif ($oParserState->strlen($sValue) === 4) {
+ $sValue = $sValue[0] . $sValue[0] . $sValue[1] . $sValue[1] . $sValue[2] . $sValue[2] . $sValue[3]
+ . $sValue[3];
+ }
+
+ if ($oParserState->strlen($sValue) === 8) {
+ $aColor = [
+ 'r' => new Size(intval($sValue[0] . $sValue[1], 16), null, true, $oParserState->currentLine()),
+ 'g' => new Size(intval($sValue[2] . $sValue[3], 16), null, true, $oParserState->currentLine()),
+ 'b' => new Size(intval($sValue[4] . $sValue[5], 16), null, true, $oParserState->currentLine()),
+ 'a' => new Size(
+ round(self::mapRange(intval($sValue[6] . $sValue[7], 16), 0, 255, 0, 1), 2),
+ null,
+ true,
+ $oParserState->currentLine()
+ ),
+ ];
+ } else {
+ $aColor = [
+ 'r' => new Size(intval($sValue[0] . $sValue[1], 16), null, true, $oParserState->currentLine()),
+ 'g' => new Size(intval($sValue[2] . $sValue[3], 16), null, true, $oParserState->currentLine()),
+ 'b' => new Size(intval($sValue[4] . $sValue[5], 16), null, true, $oParserState->currentLine()),
+ ];
+ }
+ } else {
+ $sColorMode = $oParserState->parseIdentifier(true);
+ $oParserState->consumeWhiteSpace();
+ $oParserState->consume('(');
+
+ $bContainsVar = false;
+ $iLength = $oParserState->strlen($sColorMode);
+ for ($i = 0; $i < $iLength; ++$i) {
+ $oParserState->consumeWhiteSpace();
+ if ($oParserState->comes('var')) {
+ $aColor[$sColorMode[$i]] = CSSFunction::parseIdentifierOrFunction($oParserState);
+ $bContainsVar = true;
+ } else {
+ $aColor[$sColorMode[$i]] = Size::parse($oParserState, true);
+ }
+
+ if ($bContainsVar && $oParserState->comes(')')) {
+ // With a var argument the function can have fewer arguments
+ break;
+ }
+
+ $oParserState->consumeWhiteSpace();
+ if ($i < ($iLength - 1)) {
+ $oParserState->consume(',');
+ }
+ }
+ $oParserState->consume(')');
+
+ if ($bContainsVar) {
+ return new CSSFunction($sColorMode, array_values($aColor), ',', $oParserState->currentLine());
+ }
+ }
+ return new Color($aColor, $oParserState->currentLine());
+ }
+
+ /**
+ * @param float $fVal
+ * @param float $fFromMin
+ * @param float $fFromMax
+ * @param float $fToMin
+ * @param float $fToMax
+ *
+ * @return float
+ */
+ private static function mapRange($fVal, $fFromMin, $fFromMax, $fToMin, $fToMax)
+ {
+ $fFromRange = $fFromMax - $fFromMin;
+ $fToRange = $fToMax - $fToMin;
+ $fMultiplier = $fToRange / $fFromRange;
+ $fNewVal = $fVal - $fFromMin;
+ $fNewVal *= $fMultiplier;
+ return $fNewVal + $fToMin;
+ }
+
+ /**
+ * @return array
+ */
+ public function getColor()
+ {
+ return $this->aComponents;
+ }
+
+ /**
+ * @param array $aColor
+ *
+ * @return void
+ */
+ public function setColor(array $aColor)
+ {
+ $this->setName(implode('', array_keys($aColor)));
+ $this->aComponents = $aColor;
+ }
+
+ /**
+ * @return string
+ */
+ public function getColorDescription()
+ {
+ return $this->getName();
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ // Shorthand RGB color values
+ if ($oOutputFormat->getRGBHashNotation() && implode('', array_keys($this->aComponents)) === 'rgb') {
+ $sResult = sprintf(
+ '%02x%02x%02x',
+ $this->aComponents['r']->getSize(),
+ $this->aComponents['g']->getSize(),
+ $this->aComponents['b']->getSize()
+ );
+ return '#' . (($sResult[0] == $sResult[1]) && ($sResult[2] == $sResult[3]) && ($sResult[4] == $sResult[5])
+ ? "$sResult[0]$sResult[2]$sResult[4]" : $sResult);
+ }
+ return parent::render($oOutputFormat);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/LineName.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/LineName.php
new file mode 100644
index 000000000..e231ce38f
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/LineName.php
@@ -0,0 +1,65 @@
+ $aComponents
+ * @param int $iLineNo
+ */
+ public function __construct(array $aComponents = [], $iLineNo = 0)
+ {
+ parent::__construct($aComponents, ' ', $iLineNo);
+ }
+
+ /**
+ * @return LineName
+ *
+ * @throws UnexpectedTokenException
+ * @throws UnexpectedEOFException
+ */
+ public static function parse(ParserState $oParserState)
+ {
+ $oParserState->consume('[');
+ $oParserState->consumeWhiteSpace();
+ $aNames = [];
+ do {
+ if ($oParserState->getSettings()->bLenientParsing) {
+ try {
+ $aNames[] = $oParserState->parseIdentifier();
+ } catch (UnexpectedTokenException $e) {
+ if (!$oParserState->comes(']')) {
+ throw $e;
+ }
+ }
+ } else {
+ $aNames[] = $oParserState->parseIdentifier();
+ }
+ $oParserState->consumeWhiteSpace();
+ } while (!$oParserState->comes(']'));
+ $oParserState->consume(']');
+ return new LineName($aNames, $oParserState->currentLine());
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ return '[' . parent::render(OutputFormat::createCompact()) . ']';
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php
new file mode 100644
index 000000000..055a43975
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php
@@ -0,0 +1,14 @@
+
+ */
+ const ABSOLUTE_SIZE_UNITS = ['px', 'cm', 'mm', 'mozmm', 'in', 'pt', 'pc', 'vh', 'vw', 'vmin', 'vmax', 'rem'];
+
+ /**
+ * @var array
+ */
+ const RELATIVE_SIZE_UNITS = ['%', 'em', 'ex', 'ch', 'fr'];
+
+ /**
+ * @var array
+ */
+ const NON_SIZE_UNITS = ['deg', 'grad', 'rad', 's', 'ms', 'turns', 'Hz', 'kHz'];
+
+ /**
+ * @var array>|null
+ */
+ private static $SIZE_UNITS = null;
+
+ /**
+ * @var float
+ */
+ private $fSize;
+
+ /**
+ * @var string|null
+ */
+ private $sUnit;
+
+ /**
+ * @var bool
+ */
+ private $bIsColorComponent;
+
+ /**
+ * @param float|int|string $fSize
+ * @param string|null $sUnit
+ * @param bool $bIsColorComponent
+ * @param int $iLineNo
+ */
+ public function __construct($fSize, $sUnit = null, $bIsColorComponent = false, $iLineNo = 0)
+ {
+ parent::__construct($iLineNo);
+ $this->fSize = (float)$fSize;
+ $this->sUnit = $sUnit;
+ $this->bIsColorComponent = $bIsColorComponent;
+ }
+
+ /**
+ * @param bool $bIsColorComponent
+ *
+ * @return Size
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public static function parse(ParserState $oParserState, $bIsColorComponent = false)
+ {
+ $sSize = '';
+ if ($oParserState->comes('-')) {
+ $sSize .= $oParserState->consume('-');
+ }
+ while (is_numeric($oParserState->peek()) || $oParserState->comes('.')) {
+ if ($oParserState->comes('.')) {
+ $sSize .= $oParserState->consume('.');
+ } else {
+ $sSize .= $oParserState->consume(1);
+ }
+ }
+
+ $sUnit = null;
+ $aSizeUnits = self::getSizeUnits();
+ foreach ($aSizeUnits as $iLength => &$aValues) {
+ $sKey = strtolower($oParserState->peek($iLength));
+ if (array_key_exists($sKey, $aValues)) {
+ if (($sUnit = $aValues[$sKey]) !== null) {
+ $oParserState->consume($iLength);
+ break;
+ }
+ }
+ }
+ return new Size((float)$sSize, $sUnit, $bIsColorComponent, $oParserState->currentLine());
+ }
+
+ /**
+ * @return array>
+ */
+ private static function getSizeUnits()
+ {
+ if (!is_array(self::$SIZE_UNITS)) {
+ self::$SIZE_UNITS = [];
+ foreach (array_merge(self::ABSOLUTE_SIZE_UNITS, self::RELATIVE_SIZE_UNITS, self::NON_SIZE_UNITS) as $val) {
+ $iSize = strlen($val);
+ if (!isset(self::$SIZE_UNITS[$iSize])) {
+ self::$SIZE_UNITS[$iSize] = [];
+ }
+ self::$SIZE_UNITS[$iSize][strtolower($val)] = $val;
+ }
+
+ krsort(self::$SIZE_UNITS, SORT_NUMERIC);
+ }
+
+ return self::$SIZE_UNITS;
+ }
+
+ /**
+ * @param string $sUnit
+ *
+ * @return void
+ */
+ public function setUnit($sUnit)
+ {
+ $this->sUnit = $sUnit;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getUnit()
+ {
+ return $this->sUnit;
+ }
+
+ /**
+ * @param float|int|string $fSize
+ */
+ public function setSize($fSize)
+ {
+ $this->fSize = (float)$fSize;
+ }
+
+ /**
+ * @return float
+ */
+ public function getSize()
+ {
+ return $this->fSize;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isColorComponent()
+ {
+ return $this->bIsColorComponent;
+ }
+
+ /**
+ * Returns whether the number stored in this Size really represents a size (as in a length of something on screen).
+ *
+ * @return false if the unit an angle, a duration, a frequency or the number is a component in a Color object.
+ */
+ public function isSize()
+ {
+ if (in_array($this->sUnit, self::NON_SIZE_UNITS, true)) {
+ return false;
+ }
+ return !$this->isColorComponent();
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRelative()
+ {
+ if (in_array($this->sUnit, self::RELATIVE_SIZE_UNITS, true)) {
+ return true;
+ }
+ if ($this->sUnit === null && $this->fSize != 0) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ $l = localeconv();
+ $sPoint = preg_quote($l['decimal_point'], '/');
+ $sSize = preg_match("/[\d\.]+e[+-]?\d+/i", (string)$this->fSize)
+ ? preg_replace("/$sPoint?0+$/", "", sprintf("%f", $this->fSize)) : $this->fSize;
+ return preg_replace(["/$sPoint/", "/^(-?)0\./"], ['.', '$1.'], $sSize)
+ . ($this->sUnit === null ? '' : $this->sUnit);
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/URL.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/URL.php
new file mode 100644
index 000000000..1467d505c
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/URL.php
@@ -0,0 +1,82 @@
+oURL = $oURL;
+ }
+
+ /**
+ * @return URL
+ *
+ * @throws SourceException
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public static function parse(ParserState $oParserState)
+ {
+ $bUseUrl = $oParserState->comes('url', true);
+ if ($bUseUrl) {
+ $oParserState->consume('url');
+ $oParserState->consumeWhiteSpace();
+ $oParserState->consume('(');
+ }
+ $oParserState->consumeWhiteSpace();
+ $oResult = new URL(CSSString::parse($oParserState), $oParserState->currentLine());
+ if ($bUseUrl) {
+ $oParserState->consumeWhiteSpace();
+ $oParserState->consume(')');
+ }
+ return $oResult;
+ }
+
+ /**
+ * @return void
+ */
+ public function setURL(CSSString $oURL)
+ {
+ $this->oURL = $oURL;
+ }
+
+ /**
+ * @return CSSString
+ */
+ public function getURL()
+ {
+ return $this->oURL;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ return "url({$this->oURL->render($oOutputFormat)})";
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Value.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Value.php
new file mode 100644
index 000000000..66cb9fd47
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Value.php
@@ -0,0 +1,198 @@
+iLineNo = $iLineNo;
+ }
+
+ /**
+ * @param array $aListDelimiters
+ *
+ * @return RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string
+ *
+ * @throws UnexpectedTokenException
+ * @throws UnexpectedEOFException
+ */
+ public static function parseValue(ParserState $oParserState, array $aListDelimiters = [])
+ {
+ /** @var array $aStack */
+ $aStack = [];
+ $oParserState->consumeWhiteSpace();
+ //Build a list of delimiters and parsed values
+ while (
+ !($oParserState->comes('}') || $oParserState->comes(';') || $oParserState->comes('!')
+ || $oParserState->comes(')')
+ || $oParserState->comes('\\'))
+ ) {
+ if (count($aStack) > 0) {
+ $bFoundDelimiter = false;
+ foreach ($aListDelimiters as $sDelimiter) {
+ if ($oParserState->comes($sDelimiter)) {
+ array_push($aStack, $oParserState->consume($sDelimiter));
+ $oParserState->consumeWhiteSpace();
+ $bFoundDelimiter = true;
+ break;
+ }
+ }
+ if (!$bFoundDelimiter) {
+ //Whitespace was the list delimiter
+ array_push($aStack, ' ');
+ }
+ }
+ array_push($aStack, self::parsePrimitiveValue($oParserState));
+ $oParserState->consumeWhiteSpace();
+ }
+ // Convert the list to list objects
+ foreach ($aListDelimiters as $sDelimiter) {
+ if (count($aStack) === 1) {
+ return $aStack[0];
+ }
+ $iStartPosition = null;
+ while (($iStartPosition = array_search($sDelimiter, $aStack, true)) !== false) {
+ $iLength = 2; //Number of elements to be joined
+ for ($i = $iStartPosition + 2; $i < count($aStack); $i += 2, ++$iLength) {
+ if ($sDelimiter !== $aStack[$i]) {
+ break;
+ }
+ }
+ $oList = new RuleValueList($sDelimiter, $oParserState->currentLine());
+ for ($i = $iStartPosition - 1; $i - $iStartPosition + 1 < $iLength * 2; $i += 2) {
+ $oList->addListComponent($aStack[$i]);
+ }
+ array_splice($aStack, $iStartPosition - 1, $iLength * 2 - 1, [$oList]);
+ }
+ }
+ if (!isset($aStack[0])) {
+ throw new UnexpectedTokenException(
+ " {$oParserState->peek()} ",
+ $oParserState->peek(1, -1) . $oParserState->peek(2),
+ 'literal',
+ $oParserState->currentLine()
+ );
+ }
+ return $aStack[0];
+ }
+
+ /**
+ * @param bool $bIgnoreCase
+ *
+ * @return CSSFunction|string
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public static function parseIdentifierOrFunction(ParserState $oParserState, $bIgnoreCase = false)
+ {
+ $sResult = $oParserState->parseIdentifier($bIgnoreCase);
+
+ if ($oParserState->comes('(')) {
+ $oParserState->consume('(');
+ $aArguments = Value::parseValue($oParserState, ['=', ' ', ',']);
+ $sResult = new CSSFunction($sResult, $aArguments, ',', $oParserState->currentLine());
+ $oParserState->consume(')');
+ }
+
+ return $sResult;
+ }
+
+ /**
+ * @return CSSFunction|CSSString|LineName|Size|URL|string
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ * @throws SourceException
+ */
+ public static function parsePrimitiveValue(ParserState $oParserState)
+ {
+ $oValue = null;
+ $oParserState->consumeWhiteSpace();
+ if (
+ is_numeric($oParserState->peek())
+ || ($oParserState->comes('-.')
+ && is_numeric($oParserState->peek(1, 2)))
+ || (($oParserState->comes('-') || $oParserState->comes('.')) && is_numeric($oParserState->peek(1, 1)))
+ ) {
+ $oValue = Size::parse($oParserState);
+ } elseif ($oParserState->comes('#') || $oParserState->comes('rgb', true) || $oParserState->comes('hsl', true)) {
+ $oValue = Color::parse($oParserState);
+ } elseif ($oParserState->comes('url', true)) {
+ $oValue = URL::parse($oParserState);
+ } elseif (
+ $oParserState->comes('calc', true) || $oParserState->comes('-webkit-calc', true)
+ || $oParserState->comes('-moz-calc', true)
+ ) {
+ $oValue = CalcFunction::parse($oParserState);
+ } elseif ($oParserState->comes("'") || $oParserState->comes('"')) {
+ $oValue = CSSString::parse($oParserState);
+ } elseif ($oParserState->comes("progid:") && $oParserState->getSettings()->bLenientParsing) {
+ $oValue = self::parseMicrosoftFilter($oParserState);
+ } elseif ($oParserState->comes("[")) {
+ $oValue = LineName::parse($oParserState);
+ } elseif ($oParserState->comes("U+")) {
+ $oValue = self::parseUnicodeRangeValue($oParserState);
+ } else {
+ $oValue = self::parseIdentifierOrFunction($oParserState);
+ }
+ $oParserState->consumeWhiteSpace();
+ return $oValue;
+ }
+
+ /**
+ * @return CSSFunction
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ private static function parseMicrosoftFilter(ParserState $oParserState)
+ {
+ $sFunction = $oParserState->consumeUntil('(', false, true);
+ $aArguments = Value::parseValue($oParserState, [',', '=']);
+ return new CSSFunction($sFunction, $aArguments, ',', $oParserState->currentLine());
+ }
+
+ /**
+ * @return string
+ *
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ private static function parseUnicodeRangeValue(ParserState $oParserState)
+ {
+ $iCodepointMaxLength = 6; // Code points outside BMP can use up to six digits
+ $sRange = "";
+ $oParserState->consume("U+");
+ do {
+ if ($oParserState->comes('-')) {
+ $iCodepointMaxLength = 13; // Max length is 2 six digit code points + the dash(-) between them
+ }
+ $sRange .= $oParserState->consume(1);
+ } while (strlen($sRange) < $iCodepointMaxLength && preg_match("/[A-Fa-f0-9\?-]/", $oParserState->peek()));
+ return "U+{$sRange}";
+ }
+
+ /**
+ * @return int
+ */
+ public function getLineNo()
+ {
+ return $this->iLineNo;
+ }
+}
diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/ValueList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/ValueList.php
new file mode 100644
index 000000000..af5348b96
--- /dev/null
+++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/ValueList.php
@@ -0,0 +1,100 @@
+
+ */
+ protected $aComponents;
+
+ /**
+ * @var string
+ */
+ protected $sSeparator;
+
+ /**
+ * phpcs:ignore Generic.Files.LineLength
+ * @param array|RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string $aComponents
+ * @param string $sSeparator
+ * @param int $iLineNo
+ */
+ public function __construct($aComponents = [], $sSeparator = ',', $iLineNo = 0)
+ {
+ parent::__construct($iLineNo);
+ if (!is_array($aComponents)) {
+ $aComponents = [$aComponents];
+ }
+ $this->aComponents = $aComponents;
+ $this->sSeparator = $sSeparator;
+ }
+
+ /**
+ * @param RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string $mComponent
+ *
+ * @return void
+ */
+ public function addListComponent($mComponent)
+ {
+ $this->aComponents[] = $mComponent;
+ }
+
+ /**
+ * @return array
+ */
+ public function getListComponents()
+ {
+ return $this->aComponents;
+ }
+
+ /**
+ * @param array $aComponents
+ *
+ * @return void
+ */
+ public function setListComponents(array $aComponents)
+ {
+ $this->aComponents = $aComponents;
+ }
+
+ /**
+ * @return string
+ */
+ public function getListSeparator()
+ {
+ return $this->sSeparator;
+ }
+
+ /**
+ * @param string $sSeparator
+ *
+ * @return void
+ */
+ public function setListSeparator($sSeparator)
+ {
+ $this->sSeparator = $sSeparator;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render(new OutputFormat());
+ }
+
+ /**
+ * @return string
+ */
+ public function render(OutputFormat $oOutputFormat)
+ {
+ return $oOutputFormat->implode(
+ $oOutputFormat->spaceBeforeListArgumentSeparator($this->sSeparator) . $this->sSeparator
+ . $oOutputFormat->spaceAfterListArgumentSeparator($this->sSeparator),
+ $this->aComponents
+ );
+ }
+}