File manager - Edit - /home/opticamezl/www/newok/dompdf.tar
Back
dompdf/lib/Cpdf.php 0000604 00000571261 15173213354 0010164 0 ustar 00 <?php /** * 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 <pdf@ros.co.nz> * @author Orion Richardson <orionr@yahoo.com> * @author Helmut Tischer <htischer@weihenstephan.org> * @author Ryan H. Masten <ryan.masten@gmail.com> * @author Brian Sweeney <eclecticgeek@gmail.com> * @author Fabien Ménager <fabien.menager@gmail.com> * @license Public Domain http://creativecommons.org/licenses/publicdomain/ * @package Cpdf */ use FontLib\Font; use FontLib\BinaryStream; class Cpdf { /** * @var integer The current number of pdf objects in the document */ public $numObj = 0; /** * @var array This array contains all of the pdf objects, ready for final assembly */ public $objects = array(); /** * @var integer The objectId (number within the objects array) of the document catalog */ public $catalogId; /** * @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(); /** * @var string The default font metrics file to use if no other font has been loaded. * The path to the directory containing the font metrics should be included */ public $defaultFont = './fonts/Helvetica.afm'; /** * @string A record of the current font */ public $currentFont = ''; /** * @var string The current base font */ public $currentBaseFont = ''; /** * @var integer The number of the current font within the font array */ public $currentFontNum = 0; /** * @var integer */ public $currentNode; /** * @var integer Object number of the current page */ public $currentPage; /** * @var integer Object number of the currently active contents block */ public $currentContents; /** * @var integer Number of fonts within the system */ public $numFonts = 0; /** * @var integer Number of graphic state resources used */ private $numStates = 0; /** * @var array Number of graphic state resources used */ private $gstates = array(); /** * @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 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 */ public $currentLineStyle = ''; /** * @var array Current line transparency (partial graphics state) */ public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0); /** * array Current fill transparency (partial graphics state) */ public $currentFillTransparency = array("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(); /** * @var integer Number of elements within the state stack */ public $nStateStack = 0; /** * @var integer Number of page objects within the document */ public $numPages = 0; /** * @var array Object Id storage stack */ public $stack = array(); /** * @var integer Number of elements within the object Id storage stack */ public $nStack = 0; /** * 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(); /** * array contains information about how the loose objects are to be added to the document */ public $addLooseObjects = array(); /** * @var integer The objectId of the information object for the document * this contains authorship, title etc. */ public $infoObject = 0; /** * @var integer Number of images being tracked within the document */ public $numImages = 0; /** * @var array An array containing options about the document * it defaults to turning on the compression of the objects */ public $options = array('compression' => true); /** * @var integer The objectId of the first page of the document */ public $firstPageId; /** * @var integer The object Id of the procset object */ public $procsetObjectId; /** * @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 constructor function. */ public $fontFamilies = array(); /** * @var string Folder for php serialized formats of font metrics files. * If empty string, use same folder as original metrics files. * This can be passed in from class creator. * If this folder does not exist or is not writable, Cpdf will be **much** slower. * Because of potential trouble with php safe mode, folder cannot be created at runtime. */ public $fontcache = ''; /** * @var integer The version of the font metrics cache file. * This value must be manually incremented whenever the internal font data structure is modified. */ public $fontcacheVersion = 6; /** * @var string Temporary folder. * If empty string, will attempt system tmp folder. * This can be passed in from class creator. */ public $tmp = ''; /** * @var string Track if the current font is bolded or italicised */ public $currentTextState = ''; /** * @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information */ public $messages = ''; /** * @var string The encryption array for the document encryption is stored here */ public $arc4 = ''; /** * @var integer The object Id of the encryption information */ public $arc4_objnum = 0; /** * @var string The file identifier, used to uniquely identify a pdf document */ public $fileIdentifier = ''; /** * @var boolean A flag to say if a document is to be encrypted or not */ public $encrypted = false; /** * @var string The encryption key for the encryption of all the document content (structure is not encrypted) */ public $encryptionKey = ''; /** * @var array Array which forms a stack to keep track of nested callback functions */ public $callback = array(); /** * @var integer The number of callback functions in the callback array */ public $nCallback = 0; /** * @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(); /** * @var array Store the stack for the transaction commands, each item in here is a record of the values of all the * publiciables within the class, so that the user can rollback at will (from each 'start' command) * note that this includes the objects array, so these can be large. */ public $checkpoint = ''; /** * @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(); /** * @var boolean Whether the text passed in should be treated as Unicode or just local character set. */ public $isUnicode = false; /** * @var string the JavaScript code of the document */ public $javascript = ''; /** * @var boolean whether the compression is possible */ protected $compressionReady = false; /** * @var array Current page size */ protected $currentPageSize = array("width" => 0, "height" => 0); /** * @var array All the chars that will be required in the font subsets */ protected $stringSubsets = array(); /** * @var string The target internal encoding */ static protected $targetEncoding = 'Windows-1252'; /** * @var array The list of the core fonts */ static protected $coreFonts = array( 'courier', 'courier-bold', 'courier-oblique', 'courier-boldoblique', 'helvetica', 'helvetica-bold', 'helvetica-oblique', 'helvetica-boldoblique', 'times-roman', 'times-bold', 'times-italic', 'times-bolditalic', 'symbol', 'zapfdingbats' ); /** * Class constructor * This will start a new document * * @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero. * @param boolean $isUnicode Whether text will be treated as Unicode or not. * @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 = '') { $this->isUnicode = $isUnicode; $this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\"); $this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir()); $this->newDocument($pageSize); $this->compressionReady = function_exists('gzcompress'); if (in_array('Windows-1252', mb_list_encodings())) { self::$targetEncoding = 'Windows-1252'; } // also initialize the font families that are known about already $this->setFontFamily('init'); } /** * Document object methods (internal use only) * * There is about one object method for each type of object in the pdf document * Each function has the same call list ($id,$action,$options). * $id = the object ID of the object, or what it is to be if it is being created * $action = a string specifying the action to be performed, though ALL must support: * 'new' - create the object with the id $id * 'out' - produce the output for the pdf object * $options = optional, a string or array containing the various parameters for the object * * These, in conjunction with the output function are the ONLY way for output to be produced * within the pdf 'file'. */ /** * 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 = '') { switch ($action) { case 'new': $this->objects[$id] = array('t' => 'destination', 'info' => array()); $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': case 'FitB': $tmp = $options['type'] . $tmp; $this->objects[$id]['info']['string'] = $tmp; $this->objects[$id]['info']['page'] = $options['page']; } 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 = '') { switch ($action) { case 'new': $this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array()); 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': if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) { continue; } $o['info'][$k] = $v; break; case 'Direction': if (!in_array($v, array('L2R', 'R2L'))) { continue; } $o['info'][$k] = $v; break; case 'PrintScaling': if (!in_array($v, array('None', 'AppDefault'))) { continue; } $o['info'][$k] = $v; break; case 'Duplex': if (!in_array($v, array('None', 'AppDefault'))) { continue; } $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"; 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array('t' => 'catalog', 'info' => array()); $this->catalogId = $id; break; case 'outlines': case 'pages': case 'openHere': case 'javascript': $o['info'][$action] = $options; break; case 'viewerPreferences': if (!isset($o['info']['viewerPreferences'])) { $this->numObj++; $this->o_viewerPreferences($this->numObj, 'new'); $o['info']['viewerPreferences'] = $this->numObj; } $vp = $o['info']['viewerPreferences']; $this->o_viewerPreferences($vp, 'add', $options); break; case 'out': $res = "\n$id 0 obj\n<< /Type /Catalog"; foreach ($o['info'] as $k => $v) { switch ($k) { case 'outlines': $res .= "\n/Outlines $v 0 R"; break; case 'pages': $res .= "\n/Pages $v 0 R"; break; case 'viewerPreferences': $res .= "\n/ViewerPreferences $v 0 R"; break; case 'openHere': $res .= "\n/OpenAction $v 0 R"; break; case 'javascript': $res .= "\n/Names <</JavaScript $v 0 R>>"; break; } } $res .= " >>\nendobj"; 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array('t' => 'pages', 'info' => array()); $this->o_catalog($this->catalogId, 'pages', $id); break; case 'page': if (!is_array($options)) { // then it will just be the id of the new page $o['info']['pages'][] = $options; } else { // then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative // and pos is either 'before' or 'after', saying where this page will fit. if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) { $i = array_search($options['rid'], $o['info']['pages']); if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) { // then there is a match // make a space switch ($options['pos']) { case 'before': $k = $i; break; case 'after': $k = $i + 1; break; default: $k = -1; break; } if ($k >= 0) { for ($j = count($o['info']['pages']) - 1; $j >= $k; $j--) { $o['info']['pages'][$j + 1] = $o['info']['pages'][$j]; } $o['info']['pages'][$k] = $options['id']; } } } } break; case 'procset': $o['info']['procset'] = $options; break; case 'mediaBox': $o['info']['mediaBox'] = $options; // which should be an array of 4 numbers $this->currentPageSize = array('width' => $options[2], 'height' => $options[3]); break; case 'font': $o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']); break; case 'extGState': $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']); break; case 'xObject': $o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']); break; case 'out': if (count($o['info']['pages'])) { $res = "\n$id 0 obj\n<< /Type /Pages\n/Kids ["; foreach ($o['info']['pages'] as $v) { $res .= "$v 0 R\n"; } $res .= "]\n/Count " . count($this->objects[$id]['info']['pages']); if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) || isset($o['info']['procset']) || (isset($o['info']['extGStates']) && count($o['info']['extGStates'])) ) { $res .= "\n/Resources <<"; if (isset($o['info']['procset'])) { $res .= "\n/ProcSet " . $o['info']['procset'] . " 0 R"; } if (isset($o['info']['fonts']) && count($o['info']['fonts'])) { $res .= "\n/Font << "; foreach ($o['info']['fonts'] as $finfo) { $res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R"; } $res .= "\n>>"; } if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) { $res .= "\n/XObject << "; foreach ($o['info']['xObjects'] as $finfo) { $res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R"; } $res .= "\n>>"; } if (isset($o['info']['extGStates']) && count($o['info']['extGStates'])) { $res .= "\n/ExtGState << "; foreach ($o['info']['extGStates'] as $gstate) { $res .= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R"; } $res .= "\n>>"; } $res .= "\n>>"; 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 >>\nendobj"; } else { $res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj"; } 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array())); $this->o_catalog($this->catalogId, 'outlines', $id); break; case 'outline': $o['info']['outlines'][] = $options; break; case 'out': if (count($o['info']['outlines'])) { $res = "\n$id 0 obj\n<< /Type /Outlines /Kids ["; foreach ($o['info']['outlines'] as $v) { $res .= "$v 0 R "; } $res .= "] /Count " . count($o['info']['outlines']) . " >>\nendobj"; } else { $res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj"; } return $res; } return null; } /** * an object to hold the font description * * @param $id * @param $action * @param string|array $options * @return string|null */ protected function o_font($id, $action, $options = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array( 't' => 'font', 'info' => array( 'name' => $options['name'], 'fontFileName' => $options['fontFileName'], 'SubType' => 'Type1' ) ); $fontNum = $this->numFonts; $this->objects[$id]['info']['fontNum'] = $fontNum; // deal with the encoding and the differences if (isset($options['differences'])) { // then we'll need an encoding dictionary $this->numObj++; $this->o_fontEncoding($this->numObj, 'new', $options); $this->objects[$id]['info']['encodingDictionary'] = $this->numObj; } else { if (isset($options['encoding'])) { // we can specify encoding here switch ($options['encoding']) { case 'WinAnsiEncoding': case 'MacRomanEncoding': case 'MacExpertEncoding': $this->objects[$id]['info']['encoding'] = $options['encoding']; break; case 'none': break; default: $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding'; break; } } else { $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding'; } } if ($this->fonts[$options['fontFileName']]['isUnicode']) { // 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 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_toUnicode($toUnicodeId, 'new'); $this->objects[$id]['info']['toUnicode'] = $toUnicodeId; $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)); 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; } } // pass values down to descendent font if (isset($o['info']['cidFont'])) { $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options); } break; case 'out': if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) { // 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 information. // // All of this code is adapted from the excellent changes made to // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/) $res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n"; $res .= "/BaseFont /" . $o['info']['name'] . "\n"; // The horizontal identity mapping for 2-byte CIDs; may be used // with CIDFonts using any Registry, Ordering, and Supplement values. $res .= "/Encoding /Identity-H\n"; $res .= "/DescendantFonts [" . $o['info']['cidFont'] . " 0 R]\n"; $res .= "/ToUnicode " . $o['info']['toUnicode'] . " 0 R\n"; $res .= ">>\n"; $res .= "endobj"; } else { $res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /" . $o['info']['SubType'] . "\n"; $res .= "/Name /F" . $o['info']['fontNum'] . "\n"; $res .= "/BaseFont /" . $o['info']['name'] . "\n"; if (isset($o['info']['encodingDictionary'])) { // then place a reference to the dictionary $res .= "/Encoding " . $o['info']['encodingDictionary'] . " 0 R\n"; } else { if (isset($o['info']['encoding'])) { // use the specified encoding $res .= "/Encoding /" . $o['info']['encoding'] . "\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']['Widths'])) { $res .= "/Widths " . $o['info']['Widths'] . " 0 R\n"; } if (isset($o['info']['FontDescriptor'])) { $res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n"; } $res .= ">>\n"; $res .= "endobj"; } return $res; } return null; } /** * 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] = array( 't' => 'toUnicode' ); break; case 'add': break; case 'out': $ordering = '(UCS)'; $registry = '(Adobe)'; if ($this->encrypted) { $this->encryptInit($id); $ordering = $this->ARC4($ordering); $registry = $this->ARC4($registry); } $stream = <<<EOT /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo <</Registry $registry /Ordering $ordering /Supplement 0 >> def /CMapName /Adobe-Identity-UCS def /CMapType 2 def 1 begincodespacerange <0000> <FFFF> endcodespacerange 1 beginbfrange <0000> <FFFF> <0000> endbfrange endcmap CMapName currentdict /CMap defineresource pop end end EOT; $res = "\n$id 0 obj\n"; $res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options); break; case 'out': $res = "\n$id 0 obj\n<< /Type /FontDescriptor\n"; foreach ($o['info'] as $label => $value) { switch ($label) { case 'Ascent': case 'CapHeight': case 'Descent': case 'Flags': case 'ItalicAngle': case 'StemV': case 'AvgWidth': case 'Leading': case 'MaxWidth': case 'MissingWidth': case 'StemH': case 'XHeight': case 'CharSet': if (mb_strlen($value, '8bit')) { $res .= "/$label $value\n"; } break; case 'FontFile': case 'FontFile2': case 'FontFile3': $res .= "/$label $value 0 R\n"; break; case 'FontBBox': $res .= "/$label [$value[0] $value[1] $value[2] $value[3]]\n"; break; case 'FontName': $res .= "/$label /$value\n"; break; } } $res .= ">>\nendobj"; return $res; } return null; } /** * the font encoding * * @param $id * @param $action * @param string $options * @return null|string */ protected function o_fontEncoding($id, $action, $options = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': // the options array should contain 'differences' and maybe 'encoding' $this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options); break; case 'out': $res = "\n$id 0 obj\n<< /Type /Encoding\n"; if (!isset($o['info']['encoding'])) { $o['info']['encoding'] = 'WinAnsiEncoding'; } if ($o['info']['encoding'] !== 'none') { $res .= "/BaseEncoding /" . $o['info']['encoding'] . "\n"; } $res .= "/Differences \n["; $onum = -100; foreach ($o['info']['differences'] as $num => $label) { if ($num != $onum + 1) { // we cannot make use of consecutive numbering $res .= "\n$num /$label"; } else { $res .= " /$label"; } $onum = $num; } $res .= "\n]\n>>\nendobj"; 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options); // we need a CID system info section $cidSystemInfoId = ++$this->numObj; $this->o_cidSystemInfo($cidSystemInfoId, 'new'); $this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId; // and a CID to GID map $cidToGidMapId = ++$this->numObj; $this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options); $this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId; break; case 'add': foreach ($options as $k => $v) { switch ($k) { case 'BaseFont': $o['info']['name'] = $v; break; case 'FirstChar': case 'LastChar': case 'MissingWidth': case 'FontDescriptor': case 'SubType': $this->addMessage("o_fontDescendentCID $k : $v"); $o['info'][$k] = $v; break; } } // pass values down to cid to gid map $this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options); break; case 'out': $res = "\n$id 0 obj\n"; $res .= "<</Type /Font\n"; $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']['LastChar'])) { // $res.= "/LastChar ".$o['info']['LastChar']."\n"; // } if (isset($o['info']['FontDescriptor'])) { $res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n"; } if (isset($o['info']['MissingWidth'])) { $res .= "/DW " . $o['info']['MissingWidth'] . "\n"; } if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) { $cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths']; $w = ''; foreach ($cid_widths as $cid => $width) { $w .= "$cid [$width] "; } $res .= "/W [$w]\n"; } $res .= "/CIDToGIDMap " . $o['info']['cidToGidMap'] . " 0 R\n"; $res .= ">>\n"; $res .= "endobj"; 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] = array( '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 .= '<</Registry ' . $registry . "\n"; // A string identifying an issuer of character collections $res .= '/Ordering ' . $ordering . "\n"; // A string that uniquely names a character collection issued by a specific registry $res .= "/Supplement 0\n"; // The supplement number of the character collection. $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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options); break; case 'out': $res = "\n$id 0 obj\n"; $fontFileName = $o['info']['fontFileName']; $tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']); $compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) && $this->fonts[$fontFileName]['CIDtoGID_Compressed']; if (!$compressed && isset($o['raw'])) { $res .= $tmp; } else { $res .= "<<"; if (!$compressed && $this->compressionReady && $this->options['compression']) { // then implement ZLIB based compression on this content stream $compressed = true; $tmp = gzcompress($tmp, 6); } if ($compressed) { $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"; } $res .= "\nendobj"; 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array('t' => 'procset', 'info' => array('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 // obsolete, the items are required for printing to some postscript printers switch ($options) { case 'ImageB': case 'ImageC': case 'ImageI': $o['info'][$options] = 1; break; } break; case 'out': $res = "\n$id 0 obj\n["; foreach ($o['info'] as $label => $val) { $res .= "/$label "; } $res .= "]\nendobj"; 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 = '') { switch ($action) { case 'new': $this->infoObject = $id; $date = 'D:' . @date('Ymd'); $this->objects[$id] = array( 't' => 'info', 'info' => array( 'Producer' => 'CPDF (dompdf)', 'CreationDate' => $date ) ); break; case 'Title': case 'Author': case 'Subject': case 'Keywords': case 'Creator': case 'Producer': case 'CreationDate': case 'ModDate': case 'Trapped': $this->objects[$id]['info'][$action] = $options; break; case 'out': $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 ("; // dates must be outputted as-is, without Unicode transformations if ($k !== 'CreationDate' && $k !== 'ModDate') { $v = $this->filterText($v, true, false); } if ($encrypted) { $v = $this->ARC4($v); } $res .= $v; $res .= ")\n"; } $res .= ">>\nendobj"; 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': if (is_array($options)) { $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']); } else { // then assume a URI action $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI'); } break; case 'out': if ($this->encrypted) { $this->encryptInit($id); } $res = "\n$id 0 obj\n<< /Type /Action"; switch ($o['type']) { case 'ilink': if (!isset($this->destinations[(string)$o['info']['label']])) { break; } // there will be an 'label' setting, this is the name of the destination $res .= "\n/S /GoTo\n/D " . $this->destinations[(string)$o['info']['label']] . " 0 R"; break; case 'URI': $res .= "\n/S /URI\n/URI ("; if ($this->encrypted) { $res .= $this->filterText($this->ARC4($o['info']), false, false); } else { $res .= $this->filterText($o['info'], false, false); } $res .= ")"; break; } $res .= "\n>>\nendobj"; 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': // add the annotation to the current page $pageId = $this->currentPage; $this->o_page($pageId, 'annot', $id); // 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->numObj++; $this->o_action($this->numObj, 'new', $options['url']); $this->objects[$id]['info']['actionId'] = $this->numObj; break; case 'ilink': // this is to a named internal link $label = $options['label']; $this->objects[$id] = array('t' => 'annotation', 'info' => $options); $this->numObj++; $this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label)); $this->objects[$id]['info']['actionId'] = $this->numObj; break; } break; case 'out': $res = "\n$id 0 obj\n<< /Type /Annot"; switch ($o['info']['type']) { case 'link': case 'ilink': $res .= "\n/Subtype /Link"; break; } $res .= "\n/A " . $o['info']['actionId'] . " 0 R"; $res .= "\n/Border [0 0 0]"; $res .= "\n/H /I"; $res .= "\n/Rect [ "; foreach ($o['info']['rect'] as $v) { $res .= sprintf("%.4F ", $v); } $res .= "]"; $res .= "\n>>\nendobj"; 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->numPages++; $this->objects[$id] = array( 't' => 'page', 'info' => array( '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] $options['id'] = $id; $this->o_pages($this->currentNode, 'page', $options); } else { $this->o_pages($this->currentNode, 'page', $id); } $this->currentPage = $id; //make a contents object to go with this page $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->numObj; $match = ($this->numPages % 2 ? 'odd' : 'even'); foreach ($this->addLooseObjects as $oId => $target) { if ($target === 'all' || $match === $target) { $this->objects[$id]['info']['contents'][] = $oId; } } break; case 'content': $o['info']['contents'][] = $options; break; case 'annot': // add an annotation to this page if (!isset($o['info']['annot'])) { $o['info']['annot'] = array(); } // $options should contain the id of the annotation dictionary $o['info']['annot'][] = $options; break; 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'])) { $res .= "\n/Annots ["; foreach ($o['info']['annot'] as $aId) { $res .= " $aId 0 R"; } $res .= " ]"; } $count = count($o['info']['contents']); if ($count == 1) { $res .= "\n/Contents " . $o['info']['contents'][0] . " 0 R"; } else { if ($count > 1) { $res .= "\n/Contents [\n"; // reverse the page contents so added objects are below normal content //foreach (array_reverse($o['info']['contents']) as $cId) { // Back to normal now that I've got transparency working --Benj foreach ($o['info']['contents'] as $cId) { $res .= "$cId 0 R\n"; } $res .= "]"; } } $res .= "\n>>\nendobj"; 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 = '') { if ($action !== 'new') { $o = &$this->objects[$id]; } switch ($action) { case 'new': $this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array()); if (mb_strlen($options, '8bit') && intval($options)) { // then this contents is the primary for a page $this->objects[$id]['onPage'] = $options; } else { if ($options === 'raw') { // then this page contains some other type of system object $this->objects[$id]['raw'] = 1; } } break; case 'add': // add more options to the declaration foreach ($options as $k => $v) { $o['info'][$k] = $v; } case 'out': $tmp = $o['c']; $res = "\n$id 0 obj\n"; if (isset($this->objects[$id]['raw'])) { $res .= $tmp; } else { $res .= "<<"; if ($this->compressionReady && $this->options['compression']) { // then implement ZLIB based compression on this content stream $res .= " /Filter /FlateDecode"; $tmp = gzcompress($tmp, 6); } if ($this->encrypted) { $this->encryptInit($id); $tmp = $this->ARC4($tmp); } foreach ($o['info'] as $k => $v) { $res .= "\n/$k $v"; } $res .= "\n/Length " . mb_strlen($tmp, '8bit') . " >>\nstream\n$tmp\nendstream"; } $res .= "\nendobj"; return $res; } return null; } /** * @param $id * @param $action * @return string|null */ protected function o_embedjs($id, $action) { switch ($action) { case 'new': $this->objects[$id] = array( 't' => 'embedjs', 'info' => array( '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"; } $res .= "\n>>\nendobj"; return $res; } return null; } /** * @param $id * @param $action * @param string $code * @return null|string */ protected function o_javascript($id, $action, $code = '') { switch ($action) { case 'new': $this->objects[$id] = array( 't' => 'javascript', 'info' => array( 'S' => '/JavaScript', '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"; } $res .= "\n>>\nendobj"; 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 = '') { switch ($action) { case 'new': // make the new object $this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array()); $info =& $this->objects[$id]['info']; $info['Type'] = '/XObject'; $info['Subtype'] = '/Image'; $info['Width'] = $options['iw']; $info['Height'] = $options['ih']; if (isset($options['masked']) && $options['masked']) { $info['SMask'] = ($this->numObj - 1) . ' 0 R'; } if (!isset($options['type']) || $options['type'] === 'jpg') { if (!isset($options['channels'])) { $options['channels'] = 3; } switch ($options['channels']) { case 1: $info['ColorSpace'] = '/DeviceGray'; break; case 4: $info['ColorSpace'] = '/DeviceCMYK'; break; default: $info['ColorSpace'] = '/DeviceRGB'; break; } if ($info['ColorSpace'] === '/DeviceCMYK') { $info['Decode'] = '[1 0 1 0 1 0 1 0]'; } $info['Filter'] = '/DCTDecode'; $info['BitsPerComponent'] = 8; } else { if ($options['type'] === 'png') { $info['Filter'] = '/FlateDecode'; $info['DecodeParms'] = '<< /Predictor 15 /Colors ' . $options['ncolor'] . ' /Columns ' . $options['iw'] . ' /BitsPerComponent ' . $options['bitsPerComponent'] . '>>'; if ($options['isMask']) { $info['ColorSpace'] = '/DeviceGray'; } else { if (mb_strlen($options['pdata'], '8bit')) { $tmp = ' [ /Indexed /DeviceRGB ' . (mb_strlen($options['pdata'], '8bit') / 3 - 1) . ' '; $this->numObj++; $this->o_contents($this->numObj, 'new'); $this->objects[$this->numObj]['c'] = $options['pdata']; $tmp .= $this->numObj . ' 0 R'; $tmp .= ' ]'; $info['ColorSpace'] = $tmp; if (isset($options['transparency'])) { $transparency = $options['transparency']; switch ($transparency['type']) { case 'indexed': $tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] '; $info['Mask'] = $tmp; break; case 'color-key': $tmp = ' [ ' . $transparency['r'] . ' ' . $transparency['r'] . $transparency['g'] . ' ' . $transparency['g'] . $transparency['b'] . ' ' . $transparency['b'] . ' ] '; $info['Mask'] = $tmp; break; } } } else { if (isset($options['transparency'])) { $transparency = $options['transparency']; switch ($transparency['type']) { case 'indexed': $tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] '; $info['Mask'] = $tmp; break; case 'color-key': $tmp = ' [ ' . $transparency['r'] . ' ' . $transparency['r'] . ' ' . $transparency['g'] . ' ' . $transparency['g'] . ' ' . $transparency['b'] . ' ' . $transparency['b'] . ' ] '; $info['Mask'] = $tmp; break; } } $info['ColorSpace'] = '/' . $options['color']; } } $info['BitsPerComponent'] = $options['bitsPerComponent']; } } // 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)); // 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<<"; foreach ($o['info'] as $k => $v) { $res .= "\n/$k $v"; } if ($this->encrypted) { $this->encryptInit($id); $tmp = $this->ARC4($tmp); } $res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream\nendobj"; 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( "LW", "LC", "LC", "LJ", "ML", "D", "RI", "OP", "op", "OPM", "Font", "BG", "BG2", "UCR", "TR", "TR2", "HT", "FL", "SM", "SA", "BM", "SMask", "CA", "ca", "AIS", "TK" ); switch ($action) { case "new": $this->objects[$id] = array('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)); break; case "out": $o = &$this->objects[$id]; $res = "\n$id 0 obj\n<< /Type /ExtGState\n"; foreach ($o["info"] as $k => $v) { if (!in_array($k, $valid_params)) { continue; } $res .= "/$k $v\n"; } $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 = '') { switch ($action) { case 'new': // make the new object $this->objects[$id] = array('t' => 'encryption', 'info' => $options); $this->arc4_objnum = $id; break; 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); $info = $this->objects[$id]['info']; $len = mb_strlen($info['owner'], '8bit'); if ($len > 32) { $owner = substr($info['owner'], 0, 32); } else { if ($len < 32) { $owner = $info['owner'] . substr($pad, 0, 32 - $len); } else { $owner = $info['owner']; } } $len = mb_strlen($info['user'], '8bit'); if ($len > 32) { $user = substr($info['user'], 0, 32); } else { if ($len < 32) { $user = $info['user'] . substr($pad, 0, 32 - $len); } else { $user = $info['user']; } } $tmp = $this->md5_16($owner); $okey = substr($tmp, 0, 5); $this->ARC4_init($okey); $ovalue = $this->ARC4($user); $this->objects[$id]['info']['O'] = $ovalue; // now make the u value, phew. $tmp = $this->md5_16( $user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier) ); $ukey = substr($tmp, 0, 5); $this->ARC4_init($ukey); $this->encryptionKey = $ukey; $this->encrypted = true; $uvalue = $this->ARC4($pad); $this->objects[$id]['info']['U'] = $uvalue; // 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'], 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']); $res .= "\n>>\nendobj"; return $res; } return null; } /** * ARC4 functions * A series of function to implement ARC4 encoding in PHP */ /** * calculate the 16 byte version of the 128 bit md5 digest of the string * * @param $string * @return string */ function md5_16($string) { $tmp = md5($string); $out = ''; for ($i = 0; $i <= 30; $i = $i + 2) { $out .= chr(hexdec(substr($tmp, $i, 2))); } return $out; } /** * initialize the encryption for processing a particular object * * @param $id */ function encryptInit($id) { $tmp = $this->encryptionKey; $hex = dechex($id); 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) ; $key = $this->md5_16($tmp); $this->ARC4_init(substr($key, 0, 10)); } /** * initialize the ARC4 encryption * * @param string $key */ function ARC4_init($key = '') { $this->arc4 = ''; // setup the control array if (mb_strlen($key, '8bit') == 0) { return; } $k = ''; while (mb_strlen($k, '8bit') < 256) { $k .= $key; } $k = substr($k, 0, 256); for ($i = 0; $i < 256; $i++) { $this->arc4 .= chr($i); } $j = 0; for ($i = 0; $i < 256; $i++) { $t = $this->arc4[$i]; $j = ($j + ord($t) + ord($k[$i])) % 256; $this->arc4[$i] = $this->arc4[$j]; $this->arc4[$j] = $t; } } /** * ARC4 encrypt a text string * * @param $text * @return string */ function ARC4($text) { $len = mb_strlen($text, '8bit'); $a = 0; $b = 0; $c = $this->arc4; $out = ''; for ($i = 0; $i < $len; $i++) { $a = ($a + 1) % 256; $t = $c[$a]; $b = ($b + ord($t)) % 256; $c[$a] = $c[$b]; $c[$b] = $t; $k = ord($c[(ord($c[$a]) + ord($c[$b])) % 256]); $out .= chr(ord($text[$i]) ^ $k); } return $out; } /** * functions which can be called to adjust or add to the document */ /** * 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)); $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)); $this->o_annotation($this->numObj, 'new', $info); } /** * 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()) { $p = bindec("11000000"); $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32); foreach ($pc as $k => $v) { if ($v && isset($options[$k])) { $p += $options[$k]; } else { if (isset($options[$v])) { $p += $options[$v]; } } } // implement encryption on the document if ($this->arc4_objnum == 0) { // then the block does not exist already, add it. $this->numObj++; if (mb_strlen($ownerPass) == 0) { $ownerPass = $userPass; } $this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p)); } } /** * should be used for internal checks, not implemented as yet */ function checkAllHere() { } /** * return the pdf stream as a string returned from the function * * @param bool $debug * @return string */ function output($debug = false) { if ($debug) { // turn compression off $this->options['compression'] = false; } if ($this->javascript) { $this->numObj++; $js_id = $this->numObj; $this->o_embedjs($js_id, 'new'); $this->o_javascript(++$this->numObj, 'new', $this->javascript); $id = $this->catalogId; $this->o_catalog($id, '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'; $pos = mb_strlen($content, '8bit'); foreach ($this->objects as $k => $v) { $tmp = 'o_' . $v['t']; $cont = $this->$tmp($k, 'out'); $content .= $cont; $xref[] = $pos + 1; //+1 to account for \n at the start of each object $pos += mb_strlen($cont, '8bit'); } $content .= "\nxref\n0 " . (count($xref) + 1) . "\n0000000000 65535 f \n"; foreach ($xref as $p) { $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" ; // 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 .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n"; // account for \n added at start of xref table $pos++; $content .= ">>\nstartxref\n$pos\n%%EOF\n"; return $content; } /** * 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)) { $this->numObj = 0; $this->objects = array(); $this->numObj++; $this->o_catalog($this->numObj, 'new'); $this->numObj++; $this->o_outlines($this->numObj, 'new'); $this->numObj++; $this->o_pages($this->numObj, 'new'); $this->o_pages($this->numObj, 'mediaBox', $pageSize); $this->currentNode = 3; $this->numObj++; $this->o_procset($this->numObj, 'new'); $this->numObj++; $this->o_info($this->numObj, 'new'); $this->numObj++; $this->o_page($this->numObj, 'new'); // need to store the first page id as there is no way to get it to the user during // startup $this->firstPageId = $this->currentContents; } /** * open the font file and return a php structure containing it. * first check if this one has been done before and saved in a form more suited to php * 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 $name = basename($font); $dir = dirname($font) . '/'; $fontcache = $this->fontcache; if ($fontcache == '') { $fontcache = rtrim($dir, DIRECTORY_SEPARATOR."/\\"); } //$name filename without folder and extension 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 $this->addMessage("openFont: $font - $name"); if (!$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts)) { $metrics_name = "$name.afm"; } else { $metrics_name = "$name.ufm"; } $cache_name = "$metrics_name.php"; $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, '<?php return ' . $old_cache . ';'); $this->openFont($font); return; } } if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) { // then rebuild the php_<font>.afm file from the <font>.afm file $this->addMessage("openFont: build php file from $dir$metrics_name"); $data = array(); // 20 => 'space' $data['codeToName'] = array(); // 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. $data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm'); $cidtogid = ''; if ($data['isUnicode']) { $cidtogid = str_pad('', 256 * 256 * 2, "\x00"); } $file = file($dir . $metrics_name); foreach ($file as $rowA) { $row = trim($rowA); $pos = strpos($row, ' '); if ($pos) { // then there must be some keyword $key = substr($row, 0, $pos); switch ($key) { case 'FontName': case 'FullName': case 'FamilyName': case 'PostScriptName': case 'Weight': case 'ItalicAngle': case 'IsFixedPitch': case 'CharacterSet': case 'UnderlinePosition': case 'UnderlineThickness': case 'Version': case 'EncodingScheme': case 'CapHeight': case 'XHeight': case 'Ascender': case 'Descender': case 'StdHW': case 'StdVW': case 'StartCharMetrics': case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big. $data[$key] = trim(substr($row, $pos)); break; case 'FontBBox': $data[$key] = explode(' ', trim(substr($row, $pos))); break; //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; case 'C': // Found in AFM files $bits = explode(';', trim($row)); $dtmp = array(); foreach ($bits as $bit) { $bits2 = explode(' ', trim($bit)); if (mb_strlen($bits2[0], '8bit') == 0) { continue; } if (count($bits2) > 2) { $dtmp[$bits2[0]] = array(); for ($i = 1; $i < count($bits2); $i++) { $dtmp[$bits2[0]][] = $bits2[$i]; } } else { if (count($bits2) == 2) { $dtmp[$bits2[0]] = $bits2[1]; } } } $c = (int)$dtmp['C']; $n = $dtmp['N']; $width = floatval($dtmp['WX']); if ($c >= 0) { if ($c != hexdec($n)) { $data['codeToName'][$c] = $n; } $data['C'][$c] = $width; } else { $data['C'][$n] = $width; } if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') { $data['MissingWidth'] = $width; } break; // U 827 ; WX 0 ; N squaresubnosp ; G 675 ; case 'U': // Found in UFM files if (!$data['isUnicode']) { break; } $bits = explode(';', trim($row)); $dtmp = array(); foreach ($bits as $bit) { $bits2 = explode(' ', trim($bit)); if (mb_strlen($bits2[0], '8bit') === 0) { continue; } if (count($bits2) > 2) { $dtmp[$bits2[0]] = array(); for ($i = 1; $i < count($bits2); $i++) { $dtmp[$bits2[0]][] = $bits2[$i]; } } else { if (count($bits2) == 2) { $dtmp[$bits2[0]] = $bits2[1]; } } } $c = (int)$dtmp['U']; $n = $dtmp['N']; $glyph = $dtmp['G']; $width = floatval($dtmp['WX']); if ($c >= 0) { // Set values in CID to GID map if ($c >= 0 && $c < 0xFFFF && $glyph) { $cidtogid[$c * 2] = chr($glyph >> 8); $cidtogid[$c * 2 + 1] = chr($glyph & 0xFF); } if ($c != hexdec($n)) { $data['codeToName'][$c] = $n; } $data['C'][$c] = $width; } else { $data['C'][$n] = $width; } if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') { $data['MissingWidth'] = $width; } break; case 'KPX': break; // don't include them as they are not used yet //KPX Adieresis yacute -40 /*$bits = explode(' ', trim($row)); $data['KPX'][$bits[1]][$bits[2]] = $bits[3]; break;*/ } } } if ($this->compressionReady && $this->options['compression']) { // then implement ZLIB based compression on CIDtoGID string $data['CIDtoGID_Compressed'] = true; $cidtogid = gzcompress($cidtogid, 6); } $data['CIDtoGID'] = base64_encode($cidtogid); $data['_version_'] = $this->fontcacheVersion; $this->fonts[$font] = $data; //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($fontcache) && is_writable($fontcache)) { file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . var_export($data, true) . ';'); } $data = null; } if (!isset($this->fonts[$font])) { $this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?"); } //pre_r($this->messages); } /** * if the font is not loaded then load it and make the required object * else just make it the current font * the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding' * 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 $fontName * @param string $encoding * @param bool $set * @return int */ function selectFont($fontName, $encoding = '', $set = true) { $ext = substr($fontName, -4); if ($ext === '.afm' || $ext === '.ufm') { $fontName = substr($fontName, 0, mb_strlen($fontName) - 4); } if (!isset($this->fonts[$fontName])) { $this->addMessage("selectFont: selecting - $fontName - $encoding, $set"); // load the file $this->openFont($fontName); if (isset($this->fonts[$fontName])) { $this->numObj++; $this->numFonts++; $font = &$this->fonts[$fontName]; $name = basename($fontName); $dir = dirname($fontName) . '/'; $options = array('name' => $name, 'fontFileName' => $fontName); if (is_array($encoding)) { // then encoding and differences might be set if (isset($encoding['encoding'])) { $options['encoding'] = $encoding['encoding']; } if (isset($encoding['differences'])) { $options['differences'] = $encoding['differences']; } } else { if (mb_strlen($encoding, '8bit')) { // then perhaps only the encoding has been set $options['encoding'] = $encoding; } } $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); } else { $this->addMessage( 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts' ); } // 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'])) { $font['differences'] = $options['differences']; } } } if ($set && isset($this->fonts[$fontName])) { // so if for some reason the font was not set in the last one then it will not be selected $this->currentBaseFont = $fontName; // the next lines mean that if a new font is selected, then the current text state will be // applied to it as well. $this->currentFont = $this->currentBaseFont; $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum']; //$this->setCurrentFont(); } return $this->currentFontNum; //return $this->numObj; } /** * sets up the current font, based on the font families, and the current text state * 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 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. */ private function setCurrentFont() { // if (strlen($this->currentBaseFont) == 0){ // // then assume an initial font // $this->selectFont($this->defaultFont); // } // $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1); // if (strlen($this->currentTextState) // && isset($this->fontFamilies[$cf]) // && isset($this->fontFamilies[$cf][$this->currentTextState])){ // // then we are in some state or another // // and this font has a family, and the current setting exists within it // // select the font, then return it // $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState]; // $this->selectFont($nf,'',0); // $this->currentFont = $nf; // $this->currentFontNum = $this->fonts[$nf]['fontNum']; // } else { // // the this font must not have the right family member for the current state // // simply assume the base font $this->currentFont = $this->currentBaseFont; $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum']; // } } /** * 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() { return $this->firstPageId; } /** * add content to the currently active object * * @param $content */ private function addContent($content) { $this->objects[$this->currentContents]['c'] .= $content; } /** * sets the color for fill operations * * @param $color * @param bool $force */ function setColor($color, $force = false) { $new_color = array($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->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", $this->currentColor)); } } } /** * sets the color for fill operations * * @param $fillRule */ function setFillRule($fillRule) { if (!in_array($fillRule, array("nonzero", "evenodd"))) { return; } $this->fillRule = $fillRule; } /** * sets the color for stroke operations * * @param $color * @param bool $force */ function setStrokeColor($color, $force = false) { $new_color = array($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->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", $this->currentStrokeColor)); } } } /** * Set the graphics state for compositions * * @param $parameters */ function setGraphicsState($parameters) { // 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"); } /** * Set current blend mode & opacity for lines. * * Valid blend modes are: * * Normal, Multiply, Screen, Overlay, Darken, Lighten, * ColorDogde, ColorBurn, HardLight, SoftLight, Difference, * Exclusion * * @param string $mode the blend mode to use * @param float $opacity 0.0 fully transparent, 1.0 fully opaque */ function setLineTransparency($mode, $opacity) { static $blend_modes = array( "Normal", "Multiply", "Screen", "Overlay", "Darken", "Lighten", "ColorDogde", "ColorBurn", "HardLight", "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"] ) { return; } $this->currentLineTransparency["mode"] = $mode; $this->currentLineTransparency["opacity"] = $opacity; $options = array( "BM" => "/$mode", "CA" => (float)$opacity ); $this->setGraphicsState($options); } /** * Set current blend mode & opacity for filled objects. * * Valid blend modes are: * * Normal, Multiply, Screen, Overlay, Darken, Lighten, * ColorDogde, ColorBurn, HardLight, SoftLight, Difference, * Exclusion * * @param string $mode the blend mode to use * @param float $opacity 0.0 fully transparent, 1.0 fully opaque */ function setFillTransparency($mode, $opacity) { static $blend_modes = array( "Normal", "Multiply", "Screen", "Overlay", "Darken", "Lighten", "ColorDogde", "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion" ); if (!in_array($mode, $blend_modes)) { $mode = "Normal"; } if ($mode === $this->currentFillTransparency["mode"] && $opacity == $this->currentFillTransparency["opacity"] ) { return; } $this->currentFillTransparency["mode"] = $mode; $this->currentFillTransparency["opacity"] = $opacity; $options = array( "BM" => "/$mode", "ca" => (float)$opacity, ); $this->setGraphicsState($options); } /** * draw a line from one set of coordinates to another * * @param $x1 * @param $y1 * @param $x2 * @param $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 $x0 * @param $y0 * @param $x1 * @param $y1 * @param $x2 * @param $y2 * @param $x3 * @param $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 $x0 * @param $y0 * @param $astart * @param $afinish * @param $r1 * @param int $r2 * @param int $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 $x0 * @param $y0 * @param $r1 * @param int $r2 * @param int $angle * @param int $nSeg * @param int $astart * @param int $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 $x * @param $y */ function lineTo($x, $y) { $this->addContent(sprintf("\n%.3F %.3F l", $x, $y)); } /** * @param $x * @param $y */ function moveTo($x, $y) { $this->addContent(sprintf("\n%.3F %.3F m", $x, $y)); } /** * draw a bezier curve based on 4 control points * * @param $x1 * @param $y1 * @param $x2 * @param $y2 * @param $x3 * @param $y3 */ function curveTo($x1, $y1, $x2, $y2, $x3, $y3) { $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", $x1, $y1, $x2, $y2, $x3, $y3)); } /** * draw a bezier curve based on 4 control points */ function quadTo($cpx, $cpy, $x, $y) { $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y)); } function closePath() { $this->addContent(' h'); } function endPath() { $this->addContent(' n'); } /** * draw an ellipse * note that the part and filled ellipse are just special cases of this function * * draws an ellipse in the current line style * centered at $x0,$y0, radii $r1,$r2 * if $r2 is not set, then a circle is drawn * 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 $x0 * @param $y0 * @param $r1 * @param int $r2 * @param int $angle * @param int $nSeg * @param int $astart * @param int $afinish * @param bool $close * @param bool $fill * @param bool $stroke * @param bool $incomplete */ function ellipse( $x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360, $close = true, $fill = false, $stroke = true, $incomplete = false ) { if ($r1 == 0) { return; } if ($r2 == 0) { $r2 = $r1; } if ($nSeg < 2) { $nSeg = 2; } $astart = deg2rad((float)$astart); $afinish = deg2rad((float)$afinish); $totalAngle = $afinish - $astart; $dt = $totalAngle / $nSeg; $dtm = $dt / 3; if ($angle != 0) { $a = -1 * deg2rad((float)$angle); $this->addContent( sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0) ); $x0 = 0; $y0 = 0; } $t1 = $astart; $a0 = $x0 + $r1 * cos($t1); $b0 = $y0 + $r2 * sin($t1); $c0 = -$r1 * sin($t1); $d0 = $r2 * cos($t1); if (!$incomplete) { $this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0)); } for ($i = 1; $i <= $nSeg; $i++) { // draw this bit of the total curve $t1 = $i * $dt + $astart; $a1 = $x0 + $r1 * cos($t1); $b1 = $y0 + $r2 * sin($t1); $c1 = -$r1 * sin($t1); $d1 = $r2 * cos($t1); $this->addContent( sprintf( "\n%.3F %.3F %.3F %.3F %.3F %.3F c", ($a0 + $c0 * $dtm), ($b0 + $d0 * $dtm), ($a1 - $c1 * $dtm), ($b1 - $d1 * $dtm), $a1, $b1 ) ); $a0 = $a1; $b0 = $b1; $c0 = $c1; $d0 = $d1; } if (!$incomplete) { if ($fill) { $this->addContent(' f'); } if ($stroke) { if ($close) { $this->addContent(' s'); // small 's' signifies closing the path as well } else { $this->addContent(' S'); } } } if ($angle != 0) { $this->addContent(' Q'); } } /** * this sets the line drawing style. * width, is the thickness of the line in user units * cap is the type of cap to put on the line, values can be 'butt','round','square' * where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the * end of the line. * join can be 'miter', 'round', 'bevel' * dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the * on and off dashes. * (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 int $width * @param string $cap * @param string $join * @param string $dash * @param int $phase */ function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0) { // this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day $string = ''; if ($width > 0) { $string .= "$width w"; } $ca = array('butt' => 0, 'round' => 1, 'square' => 2); if (isset($ca[$cap])) { $string .= " $ca[$cap] J"; } $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); if (isset($ja[$join])) { $string .= " $ja[$join] j"; } if (is_array($dash)) { $string .= ' [ ' . implode(' ', $dash) . " ] $phase d"; } $this->currentLineStyle = $string; $this->addContent("\n$string"); } /** * draw a polygon, the syntax for this is similar to the GD polygon command * * @param $p * @param $np * @param bool $f */ function polygon($p, $np, $f = false) { $this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1])); for ($i = 2; $i < $np * 2; $i = $i + 2) { $this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1])); } if ($f) { $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 $x1 * @param $y1 * @param $width * @param $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 $x1 * @param $y1 * @param $width * @param $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 $x1 * @param $y1 * @param $width * @param $height */ function rect($x1, $y1, $width, $height) { $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height)); } function stroke() { $this->addContent("\nS"); } function fill() { $this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : "")); } function fillStroke() { $this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : "")); } /** * save the current graphic state */ 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"); } /** * restore the last graphic state */ 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 $x1 * @param $y1 * @param $width * @param $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 $x1 * @param $y1 * @param $w * @param $h * @param $rTL * @param $rTR * @param $rBR * @param $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"); } /** * 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 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) ); $this->transform($tm); } /** * translate * * @param float $t_x movement to the right * @param float $t_y movement to the bottom */ function translate($t_x, $t_y) { $tm = array( 1, 0, 0, 1, $t_x, -$t_y ); $this->transform($tm); } /** * 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) { $y = $this->currentPageSize["height"] - $y; $a = deg2rad($angle); $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, ); $this->transform($tm); } /** * 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) { $y = $this->currentPageSize["height"] - $y; $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, ); $this->transform($tm); } /** * apply graphic transformations * * @param array $tm transformation matrix */ function transform($tm) { $this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm)); } /** * 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') { // if there is a state saved, then go up the stack closing them // then on the new page, re-open them with the right setings if ($this->nStateStack) { for ($i = $this->nStateStack; $i >= 1; $i--) { $this->restoreState($i); } } $this->numObj++; if ($insert) { // 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); $this->o_page($this->numObj, 'new', $opt); } else { $this->o_page($this->numObj, 'new'); } // if there is a stack saved, then put that onto the page if ($this->nStateStack) { for ($i = 1; $i <= $this->nStateStack; $i++) { $this->saveState($i); } } // and if there has been a stroke or fill color set, then transfer them if (isset($this->currentColor)) { $this->setColor($this->currentColor, true); } if (isset($this->currentStrokeColor)) { $this->setStrokeColor($this->currentStrokeColor, true); } // if there is a line style set, then put this in too if (mb_strlen($this->currentLineStyle, '8bit')) { $this->addContent("\n$this->currentLineStyle"); } // the call to the o_page object set currentContents to the present page, so this can be returned as the page id return $this->currentContents; } /** * 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 = "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; $debug = !$options['compress']; $tmp = ltrim($this->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"; $encoding = mb_detect_encoding($filename); $fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding); $fallbackfilename = str_replace("\"", "", $fallbackfilename); $encodedfilename = rawurlencode($filename); $contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\""; if ($fallbackfilename !== $filename) { $contentDisposition .= "; filename*=UTF-8''$encodedfilename"; } header($contentDisposition); echo $tmp; flush(); } /** * return the height in units of the current font in the given size * * @param $size * @return float|int */ function getFontHeight($size) { if (!$this->numFonts) { $this->selectFont($this->defaultFont); } $font = $this->fonts[$this->currentFont]; // for the current font, and the given size, what is the height of the font in user units if (isset($font['Ascender']) && isset($font['Descender'])) { $h = $font['Ascender'] - $font['Descender']; } else { $h = $font['FontBBox'][3] - $font['FontBBox'][1]; } // have to adjust by a font offset for Windows fonts. unfortunately it looks like // the bounding box calculations are wrong and I don't know why. if (isset($font['FontHeightOffset'])) { // For CourierNew from Windows this needs to be -646 to match the // Adobe native Courier font. // // For FreeMono from GNU this needs to be -337 to match the // Courier font. // // Both have been added manually to the .afm and .ufm files. $h += (int)$font['FontHeightOffset']; } return $size * $h / 1000; } /** * @param $size * @return float|int */ function getFontXHeight($size) { if (!$this->numFonts) { $this->selectFont($this->defaultFont); } $font = $this->fonts[$this->currentFont]; // for the current font, and the given size, what is the height of the font in user units if (isset($font['XHeight'])) { $xh = $font['Ascender'] - $font['Descender']; } else { $xh = $this->getFontHeight($size) / 2; } return $size * $xh / 1000; } /** * 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 $size * @return float|int */ function getFontDescender($size) { // note that this will most likely return a negative value if (!$this->numFonts) { $this->selectFont($this->defaultFont); } //$h = $this->fonts[$this->currentFont]['FontBBox'][1]; $h = $this->fonts[$this->currentFont]['Descender']; return $size * $h / 1000; } /** * 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) { if (!$this->numFonts) { $this->selectFont($this->defaultFont); } if ($convert_encoding) { $cf = $this->currentFont; if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) { $text = $this->utf8toUtf16BE($text, $bom); } else { //$text = html_entity_decode($text, ENT_QUOTES); $text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8'); } } else if ($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 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 * * @access private * @author Orion Richardson * @since January 5, 2008 * * @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(); // array containing unicode values $bytes = array(); // 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 = array(); $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 = array(); $numbytes = 1; } } else { // use replacement character for other invalid sequences $unicode[] = 0xFFFD; $bytes = array(); $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 * * @access private * @author Orion Richardson * @since January 5, 2008 * * @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) { // given this information return an array containing x and y for the end position as elements 0 and 1 $w = $this->getTextWidth($size, $text); // need to adjust for the number of spaces in this text $words = explode(' ', $text); $nspaces = count($words) - 1; $w += $wa * $nspaces; $a = deg2rad((float)$angle); return array(cos($a) * $w + $x, -sin($a) * $w + $y); } /** * Callback method used by smallCaps * * @param array $matches * * @return string */ function toUpper($matches) { return mb_strtoupper($matches[0]); } function concatMatches($matches) { $str = ""; foreach ($matches as $match) { $str .= $match[0]; } return $str; } /** * register text for font subsetting * * @param $font * @param $text */ function registerText($font, $text) { if (!$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts)) { return; } if (!isset($this->stringSubsets[$font])) { $this->stringSubsets[$font] = array(); } $this->stringSubsets[$font] = array_unique( array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text)) ); } /** * add text to the document, at a specified location, size and angle on the page * * @param $x * @param $y * @param $size * @param $text * @param int $angle * @param int $wordSpaceAdjust * @param int $charSpaceAdjust * @param bool $smallCaps */ function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false) { if (!$this->numFonts) { $this->selectFont($this->defaultFont); } $text = str_replace(array("\r", "\n"), "", $text); 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); //$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( 'x' => $x, 'y' => $y, 'angle' => $angle, 'status' => 'sol', 'p' => $this->callback[$i]['p'], 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'descender' => $this->callback[$i]['descender'] ); $func = $this->callback[$i]['f']; $this->$func($info); } } if ($angle == 0) { $this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y)); } else { $a = deg2rad((float)$angle); $this->addContent( sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y) ); } if ($wordSpaceAdjust != 0) { $this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust)); } if ($charSpaceAdjust != 0) { $this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust)); } $len = mb_strlen($text); $start = 0; if ($start < $len) { $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) { $space_scale = 1000 / $size; $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 if ($this->nCallback > 0) { for ($i = $this->nCallback; $i > 0; $i--) { // call each function $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text); $info = array( 'x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'eol', 'p' => $this->callback[$i]['p'], 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'descender' => $this->callback[$i]['descender'] ); $func = $this->callback[$i]['f']; $this->$func($info); } } } /** * 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 */ function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0) { static $ord_cache = array(); // 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 // and put them back at the end. $store_currentTextState = $this->currentTextState; if (!$this->numFonts) { $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"; // 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 $w = 0; $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 // than just the string itself $unicode = $this->utf8toCodePointsArray($text); foreach ($unicode as $char) { // check if we have to replace character if (isset($current_font['differences'][$char])) { $char = $current_font['differences'][$char]; } if (isset($current_font['C'][$char])) { $char_width = $current_font['C'][$char]; // add the character width $w += $char_width; // 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); } } else { // If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252 if ($this->isUnicode) { $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8'); } $len = mb_strlen($text, 'Windows-1252'); for ($i = 0; $i < $len; $i++) { $c = $text[$i]; $char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c)); // check if we have to replace character if (isset($current_font['differences'][$char])) { $char = $current_font['differences'][$char]; } if (isset($current_font['C'][$char])) { $char_width = $current_font['C'][$char]; // add the character width $w += $char_width; // 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); } } $this->currentTextState = $store_currentTextState; $this->setCurrentFont(); return $w * $size / 1000; } /** * this will be called at a new page to return the state to what it was on the * 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) { if ($pageEnd) { // this will be called at a new page to return the state to what it was on the // 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 $opt = $this->stateStack[$pageEnd]; // ok to use this as stack starts numbering at 1 $this->setColor($opt['col'], true); $this->setStrokeColor($opt['str'], true); $this->addContent("\n" . $opt['lin']); // $this->currentLineStyle = $opt['lin']; } else { $this->nStateStack++; $this->stateStack[$this->nStateStack] = array( 'col' => $this->currentColor, 'str' => $this->currentStrokeColor, 'lin' => $this->currentLineStyle ); } $this->save(); } /** * restore a previously saved state * * @param int $pageEnd */ function restoreState($pageEnd = 0) { if (!$pageEnd) { $n = $this->nStateStack; $this->currentColor = $this->stateStack[$n]['col']; $this->currentStrokeColor = $this->stateStack[$n]['str']; $this->addContent("\n" . $this->stateStack[$n]['lin']); $this->currentLineStyle = $this->stateStack[$n]['lin']; $this->stateStack[$n] = null; unset($this->stateStack[$n]); $this->nStateStack--; } $this->restore(); } /** * make a loose object, the output will go into this object, until it is closed, then will revert to * 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); // add a new object of the content type, to hold the data flow $this->numObj++; $this->o_contents($this->numObj, 'new'); $this->currentContents = $this->numObj; $this->looseObjects[$this->numObj] = 1; return $this->numObj; } /** * 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->currentContents = $id; // also if this object is the primary contents for a page, then set the current page to its parent if (isset($this->objects[$id]['onPage'])) { $this->currentPage = $this->objects[$id]['onPage']; } } /** * close an object */ function closeObject() { // close the object, as long as there was one open in the first place, which will be indicated by // an objectId on the stack. if ($this->nStack > 0) { $this->currentContents = $this->stack[$this->nStack]['c']; $this->currentPage = $this->stack[$this->nStack]['p']; $this->nStack--; // easier to probably not worry about removing the old entries, they will be overwritten // if there are new ones. } } /** * 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 contain it. if (isset($this->addLooseObjects[$id])) { $this->addLooseObjects[$id] = ''; } } /** * 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') { // add the specified object to the page if (isset($this->looseObjects[$id]) && $this->currentContents != $id) { // then it is a valid object, and it is not being added to itself switch ($options) { case 'all': // then this object is to be added to this page (done in the next block) and // all future new pages. $this->addLooseObjects[$id] = 'all'; case 'add': if (isset($this->objects[$this->currentContents]['onPage'])) { // then the destination contents is the primary for the page // (though this object is actually added to that page) $this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id); } break; case 'even': $this->addLooseObjects[$id] = 'even'; $pageObjectId = $this->objects[$this->currentContents]['onPage']; if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 0) { $this->addObject($id); // hacky huh :) } break; case 'odd': $this->addLooseObjects[$id] = 'odd'; $pageObjectId = $this->objects[$this->currentContents]['onPage']; if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 1) { $this->addObject($id); // hacky huh :) } break; case 'next': $this->addLooseObjects[$id] = 'all'; break; case 'nexteven': $this->addLooseObjects[$id] = 'even'; break; case 'nextodd': $this->addLooseObjects[$id] = 'odd'; break; } } } /** * 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. * * @param $obj * @return int */ function restoreSerializedObject($obj) { $obj_id = $this->openObject(); $this->objects[$obj_id] = unserialize($obj); $this->closeObject(); return $obj_id; } /** * add content to the documents info object * * @param $label * @param int $value */ function addInfo($label, $value = 0) { // this will only work if the label is one of the valid ones. // modify this so that arrays can be passed as well. // if $label is an array then assume that it is key => value pairs // 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); } } else { $this->o_info($this->infoObject, $label, $value); } } /** * 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)); } } else { $this->o_catalog($this->catalogId, 'viewerPreferences', array($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) { // return the integer represented by $num bytes from $pos within $data $ret = 0; for ($i = 0; $i < $num; $i++) { $ret *= 256; $ret += ord($data[$pos + $i]); } return $ret; } /** * Check if image already added to pdf image directory. * If yes, need not to create again (pass empty data) * * @param $imgname * @return bool */ function image_iscached($imgname) { return isset($this->imagelist[$imgname]); } /** * 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 * @throws Exception */ function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null) { if (!function_exists("imagepng")) { throw new \Exception("The PHP GD extension is required, but is not installed."); } //if already cached, need not to read again if (isset($this->imagelist[$file])) { $data = null; } else { // Example for transparency handling on new image. Retain for current image // $tIndex = imagecolortransparent($img); // if ($tIndex > 0) { // $tColor = imagecolorsforindex($img, $tIndex); // $new_tIndex = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']); // imagefill($new_img, 0, 0, $new_tIndex); // imagecolortransparent($new_img, $new_tIndex); // } // blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn //imagealphablending($img, true); //default, but explicitely set to ensure pdf compatibility imagesavealpha($img, false/*!$is_mask && !$mask*/); $error = 0; //DEBUG_IMG_TEMP //debugpng if (defined("DEBUGPNG") && DEBUGPNG) { print '[addImagePng ' . $file . ']'; } ob_start(); @imagepng($img); $data = ob_get_clean(); 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) { $this->addMessage('PNG error - (' . $file . ') ' . $errormsg); return; } } //End isset($this->imagelist[$file]) (png Duplicate removal) $this->addPngFromBuf($file, $x, $y, $w, $h, $data, $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); if ($img === false) { return; } // FIXME The pixel transformation doesn't work well with 8bit PNGs $eight_bit = ($byte & 4) !== 4; $wpx = imagesx($img); $hpx = imagesy($img); imagesavealpha($img, false); // create temp alpha file $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_"); @unlink($tempfile_plain); $tempfile_plain = "$tempfile_plain.png"; $imgalpha = imagecreate($wpx, $hpx); imagesavealpha($imgalpha, false); // generate gray scale palette (0 -> 255) for ($c = 0; $c < 256; ++$c) { imagecolorallocate($imgalpha, $c, $c, $c); } // Use PECL gmagick + Graphics Magic to process transparent PNG images if (extension_loaded("gmagick")) { $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); // Negate opacity channel $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->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); // Make opaque image $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->writeimage($tempfile_plain); $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(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0; } $imagick = new \Imagick($file); $imagick->setFormat('png'); // Get opacity channel (negative of alpha channel) if ($imagick->getImageAlphaChannel() !== 0) { $alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone(); $alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA); $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); } else { $tempfile_alpha = null; } // Make opaque image $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->writeImage($tempfile_plain); $imgplain = imagecreatefrompng($tempfile_plain); } else { // allocated colors cache $allocated_colors = array(); // extract alpha channel for ($xpx = 0; $xpx < $wpx; ++$xpx) { for ($ypx = 0; $ypx < $hpx; ++$ypx) { $color = imagecolorat($img, $xpx, $ypx); $col = imagecolorsforindex($img, $color); $alpha = $col['alpha']; if ($eight_bit) { // with gamma correction $gammacorr = 2.2; $pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255; } else { // without gamma correction $pixel = (127 - $alpha) * 2; $key = $col['red'] . $col['green'] . $col['blue']; if (!isset($allocated_colors[$key])) { $pixel_img = imagecolorallocate($img, $col['red'], $col['green'], $col['blue']); $allocated_colors[$key] = $pixel_img; } else { $pixel_img = $allocated_colors[$key]; } imagesetpixel($img, $xpx, $ypx, $pixel_img); } imagesetpixel($imgalpha, $xpx, $ypx, $pixel); } } // extract image without alpha channel $imgplain = imagecreatetruecolor($wpx, $hpx); imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx); imagedestroy($img); imagepng($imgalpha, $tempfile_alpha); imagepng($imgplain, $tempfile_plain); } // embed mask image if ($tempfile_alpha) { $this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true); imagedestroy($imgalpha); } // embed image, masked with previously embedded mask $this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, ($tempfile_alpha !== null)); imagedestroy($imgplain); // remove temp files if ($tempfile_alpha) { unlink($tempfile_alpha); } unlink($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."); } //if already cached, need not to read again if (isset($this->imagelist[$file])) { $img = null; } else { $info = file_get_contents($file, false, null, 24, 5); $meta = unpack("CbitDepth/CcolorType/CcompressionMethod/CfilterMethod/CinterlaceMethod", $info); $bit_depth = $meta["bitDepth"]; $color_type = $meta["colorType"]; // http://www.w3.org/TR/PNG/#11IHDR // 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); if ($is_alpha) { // exclude grayscale alpha $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type); return; } //png files typically contain an alpha channel. //pdf file format or class.pdf does not support alpha blending. //on alpha blended images, more transparent areas have a color near black. //This appears in the result on not storing the alpha channel. //Correct would be the box background image or its parent when transparent. //But this would make the image dependent on the background. //Therefore create an image with white background and copy in //A more natural background than black is white. //Therefore create an empty image with white background and merge the //image in with alpha blending. $imgtmp = @imagecreatefrompng($file); if (!$imgtmp) { return; } $sx = imagesx($imgtmp); $sy = imagesy($imgtmp); $img = imagecreatetruecolor($sx, $sy); imagealphablending($img, true); // @todo is it still needed ?? $ti = imagecolortransparent($imgtmp); if ($ti >= 0) { $tc = imagecolorsforindex($imgtmp, $ti); $ti = imagecolorallocate($img, $tc['red'], $tc['green'], $tc['blue']); imagefill($img, 0, 0, $ti); imagecolortransparent($img, $ti); } else { imagefill($img, 1, 1, imagecolorallocate($img, 255, 255, 255)); } imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy); imagedestroy($imgtmp); } $this->addImagePng($file, $x, $y, $w, $h, $img); 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 $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) { if (isset($this->imagelist[$file])) { $data = null; $info['width'] = $this->imagelist[$file]['w']; $info['height'] = $this->imagelist[$file]['h']; $label = $this->imagelist[$file]['label']; } else { if ($data == null) { $this->addMessage('addPngFromBuf error - data not present!'); return; } $error = 0; if (!$error) { $header = chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10); 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'; } } if (!$error) { // set pointer $p = 8; $len = mb_strlen($data, '8bit'); // cycle through the file, identifying chunks $haveHeader = 0; $info = array(); $idata = ''; $pdata = ''; while ($p < $len) { $chunkLen = $this->getBytes($data, $p, 4); $chunkType = mb_substr($data, $p + 4, 4, '8bit'); switch ($chunkType) { case 'IHDR': // this is where all the file information comes from $info['width'] = $this->getBytes($data, $p + 8, 4); $info['height'] = $this->getBytes($data, $p + 12, 4); $info['bitDepth'] = ord($data[$p + 16]); $info['colorType'] = ord($data[$p + 17]); $info['compressionMethod'] = ord($data[$p + 18]); $info['filterMethod'] = ord($data[$p + 19]); $info['interlaceMethod'] = ord($data[$p + 20]); //print_r($info); $haveHeader = 1; if ($info['compressionMethod'] != 0) { $error = 1; //debugpng if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile unsupported compression method ' . $file . ']'; } $errormsg = 'unsupported compression method'; } if ($info['filterMethod'] != 0) { $error = 1; //debugpng if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile unsupported filter method ' . $file . ']'; } $errormsg = 'unsupported filter method'; } break; case 'PLTE': $pdata .= mb_substr($data, $p + 8, $chunkLen, '8bit'); break; case 'IDAT': $idata .= mb_substr($data, $p + 8, $chunkLen, '8bit'); break; 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(); 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... */ // 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'; $trans = 0; for ($i = $chunkLen; $i >= 0; $i--) { if (ord($data[$p + 8 + $i]) == 0) { $trans = $i; } } $transparency['data'] = $trans; break; // grayscale case 0: /* corresponding to entries in the plte chunk 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]); break; // 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 */ $transparency['r'] = $this->getBytes($data, $p + 8, 2); // r from truecolor $transparency['g'] = $this->getBytes($data, $p + 10, 2); // g from truecolor $transparency['b'] = $this->getBytes($data, $p + 12, 2); // b from truecolor $transparency['type'] = 'color-key'; break; //unsupported transparency type default: if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile unsupported transparency type ' . $file . ']'; } break; } // KS End new code break; default: break; } $p += $chunkLen + 12; } if (!$haveHeader) { $error = 1; //debugpng if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile information header is missing ' . $file . ']'; } $errormsg = 'information header is missing'; } if (isset($info['interlaceMethod']) && $info['interlaceMethod']) { $error = 1; //debugpng if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']'; } $errormsg = 'There appears to be no support for interlaced images in pdf.'; } } if (!$error && $info['bitDepth'] > 8) { $error = 1; //debugpng if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']'; } $errormsg = 'only bit depth of 8 or less is supported'; } if (!$error) { switch ($info['colorType']) { case 3: $color = 'DeviceRGB'; $ncolor = 1; break; case 2: $color = 'DeviceRGB'; $ncolor = 3; break; case 0: $color = 'DeviceGray'; $ncolor = 1; break; default: $error = 1; //debugpng if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']'; } $errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.'; } } if ($error) { $this->addMessage('PNG error - (' . $file . ') ' . $errormsg); return; } //print_r($info); // so this image is ok... add it in. $this->numImages++; $im = $this->numImages; $label = "I$im"; $this->numObj++; // $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width'])); $options = array( 'label' => $label, 'data' => $idata, 'bitsPerComponent' => $info['bitDepth'], 'pdata' => $pdata, 'iw' => $info['width'], 'ih' => $info['height'], 'type' => 'png', 'color' => $color, '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']); } if ($is_mask) { return; } if ($w <= 0 && $h <= 0) { $w = $info['width']; $h = $info['height']; } if ($w <= 0) { $w = $h / $info['height'] * $info['width']; } if ($h <= 0) { $h = $w * $info['height'] / $info['width']; } $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label)); } /** * 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) { // attempt to add a jpeg image straight from a file, using no GD commands // note that this function is unable to operate on a remote file. if (!file_exists($img)) { return; } if ($this->image_iscached($img)) { $data = null; $imageWidth = $this->imagelist[$img]['w']; $imageHeight = $this->imagelist[$img]['h']; $channels = $this->imagelist[$img]['c']; } else { $tmp = getimagesize($img); $imageWidth = $tmp[0]; $imageHeight = $tmp[1]; if (isset($tmp['channels'])) { $channels = $tmp['channels']; } else { $channels = 3; } $data = file_get_contents($img); } if ($w <= 0 && $h <= 0) { $w = $imageWidth; } if ($w == 0) { $w = $h / $imageHeight * $imageWidth; } if ($h == 0) { $h = $w * $imageHeight / $imageWidth; } $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img); } /** * common code used by the two JPEG adding functions * @param $data * @param $x * @param $y * @param int $w * @param int $h * @param $imageWidth * @param $imageHeight * @param int $channels * @param $imgname */ private function addJpegImage_common( &$data, $x, $y, $w = 0, $h = 0, $imageWidth, $imageHeight, $channels = 3, $imgname ) { if ($this->image_iscached($imgname)) { $label = $this->imagelist[$imgname]['label']; //debugpng //if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']'; } else { if ($data == null) { $this->addMessage('addJpegImage_common error - (' . $imgname . ') data not present!'); return; } // note that this function is not to be called externally // it is just the common code between the GD and the file options $this->numImages++; $im = $this->numImages; $label = "I$im"; $this->numObj++; $this->o_image( $this->numObj, 'new', array( 'label' => $label, 'data' => &$data, 'iw' => $imageWidth, 'ih' => $imageHeight, 'channels' => $channels ) ); $this->imagelist[$imgname] = array( '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)); } /** * 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 parameters are: // 'XYZ' left, top, zoom // 'Fit' // 'FitH' top // 'FitV' left // 'FitR' left,bottom,right // 'FitB' // 'FitBH' top // 'FitBV' left $this->numObj++; $this->o_destination( $this->numObj, 'new', array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c) ); $id = $this->catalogId; $this->o_catalog($id, 'openHere', $this->numObj); } /** * Add JavaScript code to the PDF document * * @param string $code */ function addJavascript($code) { $this->javascript .= $code; } /** * 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) { // associates the given label with the destination, it is done this way so that a destination can be specified after // it has been linked to // styles are the same as the 'openHere' function $this->numObj++; $this->o_destination( $this->numObj, 'new', array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c) ); $id = $this->numObj; // store the label->idf relationship, note that this means that labels can be used only once $this->destinations["$label"] = $id; } /** * 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 = '') { if (!is_array($options)) { if ($family === 'init') { // set the known family groups // these font families will be used to enable bold and italic markers to be included // within text streams. html forms will be used... <b></b> <i></i> $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 { // the user is trying to set a font family // note that this can also be used to set the base ones to something else if (mb_strlen($family)) { $this->fontFamilies[$family] = $options; } } } /** * used to add messages for use in debugging * * @param $message */ function addMessage($message) { $this->messages .= $message . "\n"; } /** * a few functions which should allow the document to be treated transactionally. * * @param $action */ function transaction($action) { switch ($action) { case 'start': // store all the data away into the checkpoint variable $data = get_object_vars($this); $this->checkpoint = $data; unset($data); break; case 'commit': if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) { $tmp = $this->checkpoint['checkpoint']; $this->checkpoint = $tmp; unset($tmp); } else { $this->checkpoint = ''; } break; case 'rewind': // do not destroy the current checkpoint, but move us back to the state then, so that we can try again if (is_array($this->checkpoint)) { // can only abort if were inside a checkpoint $tmp = $this->checkpoint; foreach ($tmp as $k => $v) { if ($k !== 'checkpoint') { $this->$k = $v; } } unset($tmp); } break; case 'abort': if (is_array($this->checkpoint)) { // can only abort if were inside a checkpoint $tmp = $this->checkpoint; foreach ($tmp as $k => $v) { $this->$k = $v; } unset($tmp); } break; } } } dompdf/lib/res/html.css 0000604 00000016663 15173213354 0011046 0 ustar 00 /** * dompdf default stylesheet. * * @package dompdf * @link http://dompdf.github.com/ * @author Benj Carson <benjcarson@digitaljunkies.ca> * @author Blake Ross <BlakeR1234@aol.com> * @author Fabien Ménager <fabien.menager@gmail.com> * @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 * * Portions from W3C * @link https://drafts.csswg.org/css-ui-3/#default-style-sheet * */ @page { margin: 1.2cm; } html { display: -dompdf-page !important; counter-reset: page; } /* blocks */ article, aside, details, div, dt, figcaption, footer, form, header, hgroup, main, nav, noscript, section, summary { display: block; } body { page-break-before: avoid; display: block; counter-increment: page; } p, dl, multicol { display: block; margin: 1em 0; } dd { display: block; margin-left: 40px; } blockquote, figure { display: block; margin: 1em 40px; } address { display: block; font-style: italic; } center { display: block; text-align: center; } blockquote[type=cite] { display: block; margin: 1em 0; padding-left: 1em; border-left: solid; border-color: blue; border-width: thin; } h1, h2, h3, h4, h5, h6 { display: block; font-weight: bold; } h1 { font-size: 2em; margin: .67em 0; } h2 { font-size: 1.5em; margin: .83em 0; } h3 { font-size: 1.17em; margin: 1em 0; } h4 { margin: 1.33em 0; } h5 { font-size: 0.83em; margin: 1.67em 0; } h6 { font-size: 0.67em; margin: 2.33em 0; } listing { display: block; font-family: fixed; font-size: medium; white-space: pre; margin: 1em 0; } plaintext, pre, xmp { display: block; font-family: fixed; white-space: pre; margin: 1em 0; } /* tables */ table { display: table; border-spacing: 2px; border-collapse: separate; margin-top: 0; margin-bottom: 0; text-indent: 0; text-align: left; /* quirk */ } table[border] { border-style: outset; border-color: gray; } /* This won't work (???) */ /* table[border] td, table[border] th { border: 1pt solid grey; }*/ /* make sure backgrounds are inherited in tables -- see bug 4510 */ td, th, tr { background: inherit; } /* caption inherits from table not table-outer */ caption { display: table-caption; text-align: center; } tr { display: table-row; vertical-align: inherit; } col { display: table-column; } colgroup { display: table-column-group; } tbody { display: table-row-group; vertical-align: middle; } thead { display: table-header-group; vertical-align: middle; } tfoot { display: table-footer-group; vertical-align: middle; } /* To simulate tbody auto-insertion */ table > tr { vertical-align: middle; } td { display: table-cell; vertical-align: inherit; text-align: inherit; padding: 1px; } th { display: table-cell; vertical-align: inherit; font-weight: bold; padding: 1px; } /* inlines */ q { quotes: '"' '"' "'" "'"; /* FIXME only the first level is used */ } q:before { content: open-quote; } q:after { content: close-quote; } :link { color: #00c; text-decoration: underline; } b, strong { font-weight: bolder; } i, cite, em, var, dfn { font-style: italic; } tt, code, kbd, samp { font-family: fixed; } u, ins { text-decoration: underline; } s, strike, del { text-decoration: line-through; } big { font-size: larger; } small { font-size: smaller; } sub { vertical-align: sub; font-size: smaller; line-height: normal; } sup { vertical-align: super; font-size: smaller; line-height: normal; } nobr { white-space: nowrap; } mark { background: yellow; color: black; } /* titles */ abbr[title], acronym[title] { text-decoration: dotted underline; } /* lists */ ul, menu, dir { display: block; list-style-type: disc; margin: 1em 0; padding-left: 40px; } ol { display: block; list-style-type: decimal; margin: 1em 0; padding-left: 40px; } li { display: list-item; } /*li:before { display: -dompdf-list-bullet !important; content: counter(-dompdf-default-counter) ". "; padding-right: 0.5em; }*/ /* nested lists have no top/bottom margins */ :matches(ul, ol, dir, menu, dl) ul, :matches(ul, ol, dir, menu, dl) ol, :matches(ul, ol, dir, menu, dl) dir, :matches(ul, ol, dir, menu, dl) menu, :matches(ul, ol, dir, menu, dl) dl { margin-top: 0; margin-bottom: 0; } /* 2 deep unordered lists use a circle */ :matches(ul, ol, dir, menu) ul, :matches(ul, ol, dir, menu) ul, :matches(ul, ol, dir, menu) ul, :matches(ul, ol, dir, menu) ul { list-style-type: circle; } /* 3 deep (or more) unordered lists use a square */ :matches(ul, ol, dir, menu) :matches(ul, ol, dir, menu) ul, :matches(ul, ol, dir, menu) :matches(ul, ol, dir, menu) menu, :matches(ul, ol, dir, menu) :matches(ul, ol, dir, menu) dir { list-style-type: square; } /* forms */ /* From https://drafts.csswg.org/css-ui-3/#default-style-sheet */ form { display: block; } input, button, select { display: inline-block; font-family: sans-serif; } input[type=text], input[type=password], select { width: 12em; } input[type=text], input[type=password], input[type=button], input[type=submit], input[type=reset], input[type=file], button, textarea, select { background: #FFF; border: 1px solid #999; padding: 2px; margin: 2px; } input[type=button], input[type=submit], input[type=reset], input[type=file], button { background: #CCC; text-align: center; } input[type=file] { width: 8em; } input[type=text]:before, input[type=button]:before, input[type=submit]:before, input[type=reset]:before { content: attr(value); } input[type=file]:before { content: "Choose a file"; } input[type=password][value]:before { font-family: "DejaVu Sans" !important; content: "\2022\2022\2022\2022\2022\2022\2022\2022"; line-height: 1em; } input[type=checkbox], input[type=radio], select:after { font-family: "DejaVu Sans" !important; font-size: 18px; line-height: 1; } input[type=checkbox]:before { content: "\2610"; } input[type=checkbox][checked]:before { content: "\2611"; } input[type=radio]:before { content: "\25CB"; } input[type=radio][checked]:before { content: "\25C9"; } textarea { display: block; height: 3em; overflow: hidden; font-family: monospace; white-space: pre-wrap; word-wrap: break-word; } select { position: relative!important; overflow: hidden!important; } select:after { position: absolute; right: 0; top: 0; height: 5em; width: 1.4em; text-align: center; background: #CCC; content: "\25BE"; } select option { display: none; } select option[selected] { display: inline; } fieldset { display: block; margin: 0.6em 2px 2px; padding: 0.75em; border: 1pt groove #666; position: relative; } fieldset > legend { position: absolute; top: -0.6em; left: 0.75em; padding: 0 0.3em; background: white; } legend { display: inline-block; } /* leafs */ hr { display: block; height: 0; border: 1px inset; margin: 0.5em auto 0.5em auto; } hr[size="1"] { border-style: solid none none none; } iframe { border: 2px inset; } noframes { display: block; } br { display: -dompdf-br; } img, img_generated { display: -dompdf-image !important; } dompdf_generated { display: inline; } /* hidden elements */ area, base, basefont, head, meta, script, style, title, noembed, param { display: none; -dompdf-keep: yes; } dompdf/lib/res/broken_image.png 0000604 00000001152 15173213354 0012503 0 ustar 00 �PNG IHDR @ @ XGl� sBIT��O� PLTE��������त������ス���������ި pHYs ��~� tEXtSoftware Adobe Fireworks CS4�Ӡ tEXtCreation Time 04/12/11�#� �IDATH����j�0���`�*�q��>� {�@� �`��ذ��w�Ύ�I���Ћ��I��Vսu��u�����g}_ ��,��:��� ͠l ����]� ���ͽ��p�#`ݔPr�{ n������ �&���v�D ��D� `�$� 4�<�t��X��\��P�z��a�》�mV�-<| ����# #!�pWG��˗��>�Ӳ��ȣ�@<I��e�p�������+V���E+�i��T�#���J@M:'U� 똀�y��>��X���4oޚf����Y> �@�E ��(�/r߿�HI��.�.p.�@�8P&�Ɓ6����U(;�ȓH%�O"��O��Ⱥ[��������ߥ� IEND�B`� dompdf/lib/html5lib/named-character-references.ser 0000604 00000543623 15173213355 0016171 0 ustar 00 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;}}}}}} dompdf/lib/html5lib/TreeBuilder.php 0000604 00000577404 15173213355 0013244 0 ustar 00 <?php /* Copyright 2007 Jeroen van der Meer <http://jero.net/> Copyright 2009 Edward Z. Yang <edwardzyang@thewritingpot.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. */ // 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 <pre> */ $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; } } } } dompdf/lib/html5lib/Data.php 0000604 00000010604 15173213355 0011667 0 ustar 00 <?php // warning: this file is encoded in UTF-8! class HTML5_Data { // at some point this should be moved to a .ser file. Another // possible optimization is to give UTF-8 bytes, not Unicode // codepoints // XXX: Not quite sure why it's named this; this is // actually the numeric entity dereference table. protected static $realCodepointTable = array( 0x00 => 0xFFFD, // REPLACEMENT CHARACTER 0x0D => 0x000A, // LINE FEED (LF) 0x80 => 0x20AC, // EURO SIGN ('€') 0x81 => 0x0081, // <control> 0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK ('‚') 0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK ('ƒ') 0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK ('„') 0x85 => 0x2026, // HORIZONTAL ELLIPSIS ('…') 0x86 => 0x2020, // DAGGER ('†') 0x87 => 0x2021, // DOUBLE DAGGER ('‡') 0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT ('ˆ') 0x89 => 0x2030, // PER MILLE SIGN ('‰') 0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON ('Š') 0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK ('‹') 0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE ('Œ') 0x8D => 0x008D, // <control> 0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON ('Ž') 0x8F => 0x008F, // <control> 0x90 => 0x0090, // <control> 0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK ('‘') 0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK ('’') 0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK ('“') 0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK ('”') 0x95 => 0x2022, // BULLET ('•') 0x96 => 0x2013, // EN DASH ('–') 0x97 => 0x2014, // EM DASH ('—') 0x98 => 0x02DC, // SMALL TILDE ('˜') 0x99 => 0x2122, // TRADE MARK SIGN ('™') 0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON ('š') 0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK ('›') 0x9C => 0x0153, // LATIN SMALL LIGATURE OE ('œ') 0x9D => 0x009D, // <control> 0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON ('ž') 0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS ('Ÿ') ); protected static $namedCharacterReferences; protected static $namedCharacterReferenceMaxLength; /** * Returns the "real" Unicode codepoint of a malformed character * reference. */ public static function getRealCodepoint($ref) { if (!isset(self::$realCodepointTable[$ref])) { return false; } else { return self::$realCodepointTable[$ref]; } } public static function getNamedCharacterReferences() { if (!self::$namedCharacterReferences) { self::$namedCharacterReferences = unserialize( file_get_contents(dirname(__FILE__) . '/named-character-references.ser')); } return self::$namedCharacterReferences; } /** * Converts a Unicode codepoint to sequence of UTF-8 bytes. * @note Shamelessly stolen from HTML Purifier, which is also * shamelessly stolen from Feyd (which is in public domain). */ public static function utf8chr($code) { /* We don't care: we live dangerously * if($code > 0x10FFFF or $code < 0x0 or ($code >= 0xD800 and $code <= 0xDFFF) ) { // bits are set outside the "valid" range as defined // by UNICODE 4.1.0 return "\xEF\xBF\xBD"; }*/ $y = $z = $w = 0; if ($code < 0x80) { // regular ASCII character $x = $code; } else { // set up bits for UTF-8 $x = ($code & 0x3F) | 0x80; if ($code < 0x800) { $y = (($code & 0x7FF) >> 6) | 0xC0; } else { $y = (($code & 0xFC0) >> 6) | 0x80; if ($code < 0x10000) { $z = (($code >> 12) & 0x0F) | 0xE0; } else { $z = (($code >> 12) & 0x3F) | 0x80; $w = (($code >> 18) & 0x07) | 0xF0; } } } // set up the actual character $ret = ''; if ($w) { $ret .= chr($w); } if ($z) { $ret .= chr($z); } if ($y) { $ret .= chr($y); } $ret .= chr($x); return $ret; } } dompdf/lib/html5lib/InputStream.php 0000604 00000024427 15173213355 0013301 0 ustar 00 <?php /* Copyright 2009 Geoffrey Sneddon <http://gsnedders.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. */ // Some conventions: // /* */ indicates verbatim text from the HTML 5 specification // // indicates regular comments class HTML5_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(); /** * @param $data | Data to parse * @throws Exception */ public function __construct($data) { /* 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. */ // XXX currently assuming input data is UTF-8; once we // build encoding detection this will no longer be the case // // We previously had an mbstring implementation here, but that // implementation is heavily non-conforming, so it's been // omitted. if (extension_loaded('iconv')) { // non-conforming $data = @iconv('UTF-8', 'UTF-8//IGNORE', $data); } else { // we can make a conforming native implementation throw new Exception('Not implemented, please install iconv'); } /* One leading U+FEFF BYTE ORDER MARK character must be ignored if any are present. */ if (substr($data, 0, 3) === "\xEF\xBB\xBF") { $data = substr($data, 3); } /* 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++) { $this->errors[] = array( 'type' => HTML5_Tokenizer::PARSEERROR, 'data' => 'null-character' ); } /* 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. */ $data = str_replace( array( "\0", "\r\n", "\r" ), array( "\xEF\xBF\xBD", "\n", "\n" ), $data ); /* 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. if (extension_loaded('pcre')) { $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++) { $this->errors[] = array( 'type' => HTML5_Tokenizer::PARSEERROR, 'data' => 'invalid-codepoint' ); } } else { // XXX: Need non-PCRE impl, probably using substr_count } $this->data = $data; $this->char = 0; $this->EOF = strlen($data); } /** * Returns the current line that the tokenizer is at. * * @return int */ public function getCurrentLine() { // Check the string isn't empty if ($this->EOF) { // 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; } else { // If the string is empty, we are on the first line (sorta). return 1; } } /** * Returns the current column of the current line that the tokenizer is at. * * @return int */ public function getColumnOffset() { // 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. $lastLine = strrpos($this->data, "\n", $this->char - 1 - strlen($this->data)); // 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 ($lastLine !== false) { $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine); } else { $findLengthOf = substr($this->data, 0, $this->char); } // Get the length for the string we need. if (extension_loaded('iconv')) { return iconv_strlen($findLengthOf, 'utf-8'); } elseif (extension_loaded('mbstring')) { return mb_strlen($findLengthOf, 'utf-8'); } elseif (extension_loaded('xml')) { return strlen(utf8_decode($findLengthOf)); } else { $count = count_chars($findLengthOf); // 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)); } } /** * Retrieve the currently consume character. * @note This performs bounds checking * * @return bool|string */ public function char() { return ($this->char++ < $this->EOF) ? $this->data[$this->char - 1] : false; } /** * Get all characters until EOF. * @note This performs bounds checking * * @return string|bool */ public function remainingChars() { if ($this->char < $this->EOF) { $data = substr($this->data, $this->char); $this->char = $this->EOF; return $data; } else { return false; } } /** * Matches as far as possible until we reach a certain set of bytes * and returns the matched substring. * * @param $bytes | Bytes to match. * @param null $max * @return bool|string */ public function charsUntil($bytes, $max = null) { if ($this->char < $this->EOF) { if ($max === 0 || $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; } else { return false; } } /** * Matches as far as possible with a certain set of bytes * and returns the matched substring. * * @param $bytes | Bytes to match. * @param null $max * @return bool|string */ public function charsWhile($bytes, $max = null) { if ($this->char < $this->EOF) { if ($max === 0 || $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; } else { return false; } } /** * Unconsume one character. */ public function unget() { if ($this->char <= $this->EOF) { $this->char--; } } } dompdf/lib/html5lib/Tokenizer.php 0000604 00000342067 15173213355 0013003 0 ustar 00 <?php /* Copyright 2007 Jeroen van der Meer <http://jero.net/> Copyright 2008 Edward Z. Yang <http://htmlpurifier.org/> Copyright 2009 Geoffrey Sneddon <http://gsnedders.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. */ // Some conventions: // /* */ indicates verbatim text from the HTML 5 specification // // indicates regular comments // all flags are in hyphenated form class HTML5_Tokenizer { /** * @var HTML5_InputStream * * Points to an InputStream object. */ protected $stream; /** * @var HTML5_TreeBuilder * * Tree builder that the tokenizer emits token to. */ private $tree; /** * @var int * * Current content model we are parsing as. */ protected $content_model; /** * Current token that is being built, but not yet emitted. Also * is the last token emitted, if applicable. */ protected $token; // These are constants describing the content model const PCDATA = 0; const RCDATA = 1; const CDATA = 2; const PLAINTEXT = 3; // These are constants describing tokens // XXX should probably be moved somewhere else, probably the // HTML5 class. const DOCTYPE = 0; const STARTTAG = 1; const ENDTAG = 2; const COMMENT = 3; const CHARACTER = 4; const SPACECHARACTER = 5; const EOF = 6; const PARSEERROR = 7; // These are constants representing bunches of characters. const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; const UPPER_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const LOWER_ALPHA = 'abcdefghijklmnopqrstuvwxyz'; const DIGIT = '0123456789'; const HEX = '0123456789ABCDEFabcdef'; const WHITESPACE = "\t\n\x0c "; /** * @param $data | Data to parse * @param HTML5_TreeBuilder|null $builder */ public function __construct($data, $builder = null) { $this->stream = new HTML5_InputStream($data); if (!$builder) { $this->tree = new HTML5_TreeBuilder; } else { $this->tree = $builder; } $this->content_model = self::PCDATA; } /** * @param null $context */ public function parseFragment($context = null) { $this->tree->setupContext($context); if ($this->tree->content_model) { $this->content_model = $this->tree->content_model; $this->tree->content_model = null; } $this->parse(); } // XXX maybe convert this into an iterator? regardless, this function // and the save function should go into a Parser facade of some sort /** * Performs the actual parsing of the document. */ public function parse() { // Current state $state = 'data'; // This is used to avoid having to have look-behind in the data state. $lastFourChars = ''; /** * Escape flag as specified by the HTML5 specification: "used to * control the behavior of the tokeniser. It is either true or * false, and initially must be set to the false state." */ $escape = false; //echo "\n\n"; while($state !== null) { /*echo $state . ' '; switch ($this->content_model) { case self::PCDATA: echo 'PCDATA'; break; case self::RCDATA: echo 'RCDATA'; break; case self::CDATA: echo 'CDATA'; break; case self::PLAINTEXT: echo 'PLAINTEXT'; break; } if ($escape) echo " escape"; echo "\n";*/ switch($state) { case 'data': /* Consume the next input character */ $char = $this->stream->char(); $lastFourChars .= $char; if (strlen($lastFourChars) > 4) { $lastFourChars = substr($lastFourChars, -4); } // see below for meaning $hyp_cond = !$escape && ( $this->content_model === self::RCDATA || $this->content_model === self::CDATA ); $amp_cond = !$escape && ( $this->content_model === self::PCDATA || $this->content_model === self::RCDATA ); $lt_cond = $this->content_model === self::PCDATA || ( ( $this->content_model === self::RCDATA || $this->content_model === self::CDATA ) && !$escape ); $gt_cond = $escape && ( $this->content_model === self::RCDATA || $this->content_model === self::CDATA ); if ($char === '&' && $amp_cond === true) { /* U+0026 AMPERSAND (&) When the content model flag is set to one of the PCDATA or RCDATA states and the escape flag is false: switch to the character reference data state. Otherwise: treat it as per the "anything else" entry below. */ $state = 'character reference data'; } elseif ( $char === '-' && $hyp_cond === true && $lastFourChars === '<!--' ) { /* U+002D HYPHEN-MINUS (-) If the content model flag is set to either the RCDATA state or the CDATA state, and the escape flag is false, and there are at least three characters before this one in the input stream, and the last four characters in the input stream, including this one, are U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS, and U+002D HYPHEN-MINUS ("<!--"), then set the escape flag to true. */ $escape = true; /* In any case, emit the input character as a character token. Stay in the data state. */ $this->emitToken(array( 'type' => self::CHARACTER, 'data' => '-' )); // We do the "any case" part as part of "anything else". /* U+003C LESS-THAN SIGN (<) */ } elseif ($char === '<' && $lt_cond === true) { /* When the content model flag is set to the PCDATA state: switch to the tag open state. When the content model flag is set to either the RCDATA state or the CDATA state and the escape flag is false: switch to the tag open state. Otherwise: treat it as per the "anything else" entry below. */ $state = 'tag open'; /* U+003E GREATER-THAN SIGN (>) */ } elseif ( $char === '>' && $gt_cond === true && substr($lastFourChars, 1) === '-->' ) { /* If the content model flag is set to either the RCDATA state or the CDATA state, and the escape flag is true, and the last three characters in the input stream including this one are U+002D HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"), set the escape flag to false. */ $escape = false; /* In any case, emit the input character as a character token. Stay in the data state. */ $this->emitToken(array( 'type' => self::CHARACTER, 'data' => '>' )); // We do the "any case" part as part of "anything else". } elseif ($char === false) { /* EOF Emit an end-of-file token. */ $state = null; $this->tree->emitToken(array( 'type' => self::EOF )); } elseif ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { // Directly after emitting a token you switch back to the "data // state". At that point spaceCharacters are important so they are // emitted separately. $chars = $this->stream->charsWhile(self::WHITESPACE); $this->emitToken(array( 'type' => self::SPACECHARACTER, 'data' => $char . $chars )); $lastFourChars .= $chars; if (strlen($lastFourChars) > 4) { $lastFourChars = substr($lastFourChars, -4); } } else { /* Anything else THIS IS AN OPTIMIZATION: Get as many character that otherwise would also be treated as a character token and emit it as a single character token. Stay in the data state. */ $mask = ''; if ($hyp_cond === true) { $mask .= '-'; } if ($amp_cond === true) { $mask .= '&'; } if ($lt_cond === true) { $mask .= '<'; } if ($gt_cond === true) { $mask .= '>'; } if ($mask === '') { $chars = $this->stream->remainingChars(); } else { $chars = $this->stream->charsUntil($mask); } $this->emitToken(array( 'type' => self::CHARACTER, 'data' => $char . $chars )); $lastFourChars .= $chars; if (strlen($lastFourChars) > 4) { $lastFourChars = substr($lastFourChars, -4); } $state = 'data'; } break; case 'character reference data': /* (This cannot happen if the content model flag is set to the CDATA state.) */ /* Attempt to consume a character reference, with no additional allowed character. */ $entity = $this->consumeCharacterReference(); /* If nothing is returned, emit a U+0026 AMPERSAND character token. Otherwise, emit the character token that was returned. */ // This is all done when consuming the character reference. $this->emitToken(array( 'type' => self::CHARACTER, 'data' => $entity )); /* Finally, switch to the data state. */ $state = 'data'; break; case 'tag open': $char = $this->stream->char(); switch ($this->content_model) { case self::RCDATA: case self::CDATA: /* Consume the next input character. If it is a U+002F SOLIDUS (/) character, switch to the close tag open state. Otherwise, emit a U+003C LESS-THAN SIGN character token and reconsume the current input character in the data state. */ // We consumed above. if ($char === '/') { $state = 'close tag open'; } else { $this->emitToken(array( 'type' => self::CHARACTER, 'data' => '<' )); $this->stream->unget(); $state = 'data'; } break; case self::PCDATA: /* If the content model flag is set to the PCDATA state Consume the next input character: */ // We consumed above. if ($char === '!') { /* U+0021 EXCLAMATION MARK (!) Switch to the markup declaration open state. */ $state = 'markup declaration open'; } elseif ($char === '/') { /* U+002F SOLIDUS (/) Switch to the close tag open state. */ $state = 'close tag open'; } elseif ('A' <= $char && $char <= 'Z') { /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z Create a new start tag token, set its tag name to the lowercase version of the input character (add 0x0020 to the character's code point), then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.) */ $this->token = array( 'name' => strtolower($char), 'type' => self::STARTTAG, 'attr' => array() ); $state = 'tag name'; } elseif ('a' <= $char && $char <= 'z') { /* U+0061 LATIN SMALL LETTER A through to U+007A LATIN SMALL LETTER Z Create a new start tag token, set its tag name to the input character, then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.) */ $this->token = array( 'name' => $char, 'type' => self::STARTTAG, 'attr' => array() ); $state = 'tag name'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+003E GREATER-THAN SIGN character token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-tag-name-but-got-right-bracket' )); $this->emitToken(array( 'type' => self::CHARACTER, 'data' => '<>' )); $state = 'data'; } elseif ($char === '?') { /* U+003F QUESTION MARK (?) Parse error. Switch to the bogus comment state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-tag-name-but-got-question-mark' )); $this->token = array( 'data' => '?', 'type' => self::COMMENT ); $state = 'bogus comment'; } else { /* Anything else Parse error. Emit a U+003C LESS-THAN SIGN character token and reconsume the current input character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-tag-name' )); $this->emitToken(array( 'type' => self::CHARACTER, 'data' => '<' )); $state = 'data'; $this->stream->unget(); } break; } break; case 'close tag open': if ( $this->content_model === self::RCDATA || $this->content_model === self::CDATA ) { /* If the content model flag is set to the RCDATA or CDATA states... */ $name = strtolower($this->stream->charsWhile(self::ALPHA)); $following = $this->stream->char(); $this->stream->unget(); if ( !$this->token || $this->token['name'] !== $name || $this->token['name'] === $name && !in_array($following, array("\x09", "\x0A", "\x0C", "\x20", "\x3E", "\x2F", false)) ) { /* if no start tag token has ever been emitted by this instance of the tokenizer (fragment case), or, if the next few characters do not match the tag name of the last start tag token emitted (compared in an ASCII case-insensitive manner), or if they do but they are not immediately followed by one of the following characters: * U+0009 CHARACTER TABULATION * U+000A LINE FEED (LF) * U+000C FORM FEED (FF) * U+0020 SPACE * U+003E GREATER-THAN SIGN (>) * U+002F SOLIDUS (/) * EOF ...then emit a U+003C LESS-THAN SIGN character token, a U+002F SOLIDUS character token, and switch to the data state to process the next input character. */ // XXX: Probably ought to replace in_array with $following === x ||... // We also need to emit $name now we've consumed that, as we // know it'll just be emitted as a character token. $this->emitToken(array( 'type' => self::CHARACTER, 'data' => '</' . $name )); $state = 'data'; } else { // This matches what would happen if we actually did the // otherwise below (but we can't because we've consumed too // much). // Start the end tag token with the name we already have. $this->token = array( 'name' => $name, 'type' => self::ENDTAG ); // Change to tag name state. $state = 'tag name'; } } elseif ($this->content_model === self::PCDATA) { /* Otherwise, if the content model flag is set to the PCDATA state [...]: */ $char = $this->stream->char(); if ('A' <= $char && $char <= 'Z') { /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z Create a new end tag token, set its tag name to the lowercase version of the input character (add 0x0020 to the character's code point), then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.) */ $this->token = array( 'name' => strtolower($char), 'type' => self::ENDTAG ); $state = 'tag name'; } elseif ('a' <= $char && $char <= 'z') { /* U+0061 LATIN SMALL LETTER A through to U+007A LATIN SMALL LETTER Z Create a new end tag token, set its tag name to the input character, then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.) */ $this->token = array( 'name' => $char, 'type' => self::ENDTAG ); $state = 'tag name'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-closing-tag-but-got-right-bracket' )); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F SOLIDUS character token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-closing-tag-but-got-eof' )); $this->emitToken(array( 'type' => self::CHARACTER, 'data' => '</' )); $this->stream->unget(); $state = 'data'; } else { /* Parse error. Switch to the bogus comment state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-closing-tag-but-got-char' )); $this->token = array( 'data' => $char, 'type' => self::COMMENT ); $state = 'bogus comment'; } } break; case 'tag name': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Switch to the before attribute name state. */ $state = 'before attribute name'; } elseif ($char === '/') { /* U+002F SOLIDUS (/) Switch to the self-closing start tag state. */ $state = 'self-closing start tag'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current tag token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ('A' <= $char && $char <= 'Z') { /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z Append the lowercase version of the current input character (add 0x0020 to the character's code point) to the current tag token's tag name. Stay in the tag name state. */ $chars = $this->stream->charsWhile(self::UPPER_ALPHA); $this->token['name'] .= strtolower($char . $chars); $state = 'tag name'; } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-tag-name' )); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the current input character to the current tag token's tag name. Stay in the tag name state. */ $chars = $this->stream->charsUntil("\t\n\x0C />" . self::UPPER_ALPHA); $this->token['name'] .= $char . $chars; $state = 'tag name'; } break; case 'before attribute name': /* Consume the next input character: */ $char = $this->stream->char(); // this conditional is optimized, check bottom if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the before attribute name state. */ $state = 'before attribute name'; } elseif ($char === '/') { /* U+002F SOLIDUS (/) Switch to the self-closing start tag state. */ $state = 'self-closing start tag'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current tag token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ('A' <= $char && $char <= 'Z') { /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z Start a new attribute in the current tag token. Set that attribute's name to the lowercase version of the current input character (add 0x0020 to the character's code point), and its value to the empty string. Switch to the attribute name state.*/ $this->token['attr'][] = array( 'name' => strtolower($char), 'value' => '' ); $state = 'attribute name'; } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-attribute-name-but-got-eof' )); $this->stream->unget(); $state = 'data'; } else { /* U+0022 QUOTATION MARK (") U+0027 APOSTROPHE (') U+003C LESS-THAN SIGN (<) U+003D EQUALS SIGN (=) Parse error. Treat it as per the "anything else" entry below. */ if ($char === '"' || $char === "'" || $char === '<' || $char === '=') { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'invalid-character-in-attribute-name' )); } /* Anything else Start a new attribute in the current tag token. Set that attribute's name to the current input character, and its value to the empty string. Switch to the attribute name state. */ $this->token['attr'][] = array( 'name' => $char, 'value' => '' ); $state = 'attribute name'; } break; case 'attribute name': // Consume the next input character: $char = $this->stream->char(); // this conditional is optimized, check bottom if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Switch to the after attribute name state. */ $state = 'after attribute name'; } elseif ($char === '/') { /* U+002F SOLIDUS (/) Switch to the self-closing start tag state. */ $state = 'self-closing start tag'; } elseif ($char === '=') { /* U+003D EQUALS SIGN (=) Switch to the before attribute value state. */ $state = 'before attribute value'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current tag token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ('A' <= $char && $char <= 'Z') { /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z Append the lowercase version of the current input character (add 0x0020 to the character's code point) to the current attribute's name. Stay in the attribute name state. */ $chars = $this->stream->charsWhile(self::UPPER_ALPHA); $last = count($this->token['attr']) - 1; $this->token['attr'][$last]['name'] .= strtolower($char . $chars); $state = 'attribute name'; } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-attribute-name' )); $this->stream->unget(); $state = 'data'; } else { /* U+0022 QUOTATION MARK (") U+0027 APOSTROPHE (') U+003C LESS-THAN SIGN (<) Parse error. Treat it as per the "anything else" entry below. */ if ($char === '"' || $char === "'" || $char === '<') { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'invalid-character-in-attribute-name' )); } /* Anything else Append the current input character to the current attribute's name. Stay in the attribute name state. */ $chars = $this->stream->charsUntil("\t\n\x0C /=>\"'" . self::UPPER_ALPHA); $last = count($this->token['attr']) - 1; $this->token['attr'][$last]['name'] .= $char . $chars; $state = 'attribute name'; } /* When the user agent leaves the attribute name state (and before emitting the tag token, if appropriate), the complete attribute's name must be compared to the other attributes on the same token; if there is already an attribute on the token with the exact same name, then this is a parse error and the new attribute must be dropped, along with the value that gets associated with it (if any). */ // this might be implemented in the emitToken method break; case 'after attribute name': // Consume the next input character: $char = $this->stream->char(); // this is an optimized conditional, check the bottom if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the after attribute name state. */ $state = 'after attribute name'; } elseif ($char === '/') { /* U+002F SOLIDUS (/) Switch to the self-closing start tag state. */ $state = 'self-closing start tag'; } elseif ($char === '=') { /* U+003D EQUALS SIGN (=) Switch to the before attribute value state. */ $state = 'before attribute value'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current tag token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ('A' <= $char && $char <= 'Z') { /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z Start a new attribute in the current tag token. Set that attribute's name to the lowercase version of the current input character (add 0x0020 to the character's code point), and its value to the empty string. Switch to the attribute name state. */ $this->token['attr'][] = array( 'name' => strtolower($char), 'value' => '' ); $state = 'attribute name'; } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-end-of-tag-but-got-eof' )); $this->stream->unget(); $state = 'data'; } else { /* U+0022 QUOTATION MARK (") U+0027 APOSTROPHE (') U+003C LESS-THAN SIGN(<) Parse error. Treat it as per the "anything else" entry below. */ if ($char === '"' || $char === "'" || $char === "<") { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'invalid-character-after-attribute-name' )); } /* Anything else Start a new attribute in the current tag token. Set that attribute's name to the current input character, and its value to the empty string. Switch to the attribute name state. */ $this->token['attr'][] = array( 'name' => $char, 'value' => '' ); $state = 'attribute name'; } break; case 'before attribute value': // Consume the next input character: $char = $this->stream->char(); // this is an optimized conditional if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the before attribute value state. */ $state = 'before attribute value'; } elseif ($char === '"') { /* U+0022 QUOTATION MARK (") Switch to the attribute value (double-quoted) state. */ $state = 'attribute value (double-quoted)'; } elseif ($char === '&') { /* U+0026 AMPERSAND (&) Switch to the attribute value (unquoted) state and reconsume this input character. */ $this->stream->unget(); $state = 'attribute value (unquoted)'; } elseif ($char === '\'') { /* U+0027 APOSTROPHE (') Switch to the attribute value (single-quoted) state. */ $state = 'attribute value (single-quoted)'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Emit the current tag token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-attribute-value-but-got-right-bracket' )); $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-attribute-value-but-got-eof' )); $this->stream->unget(); $state = 'data'; } else { /* U+003D EQUALS SIGN (=) * U+003C LESS-THAN SIGN (<) Parse error. Treat it as per the "anything else" entry below. */ if ($char === '=' || $char === '<') { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'equals-in-unquoted-attribute-value' )); } /* Anything else Append the current input character to the current attribute's value. Switch to the attribute value (unquoted) state. */ $last = count($this->token['attr']) - 1; $this->token['attr'][$last]['value'] .= $char; $state = 'attribute value (unquoted)'; } break; case 'attribute value (double-quoted)': // Consume the next input character: $char = $this->stream->char(); if ($char === '"') { /* U+0022 QUOTATION MARK (") Switch to the after attribute value (quoted) state. */ $state = 'after attribute value (quoted)'; } elseif ($char === '&') { /* U+0026 AMPERSAND (&) Switch to the character reference in attribute value state, with the additional allowed character being U+0022 QUOTATION MARK ("). */ $this->characterReferenceInAttributeValue('"'); } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-attribute-value-double-quote' )); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the current input character to the current attribute's value. Stay in the attribute value (double-quoted) state. */ $chars = $this->stream->charsUntil('"&'); $last = count($this->token['attr']) - 1; $this->token['attr'][$last]['value'] .= $char . $chars; $state = 'attribute value (double-quoted)'; } break; case 'attribute value (single-quoted)': // Consume the next input character: $char = $this->stream->char(); if ($char === "'") { /* U+0022 QUOTATION MARK (') Switch to the after attribute value state. */ $state = 'after attribute value (quoted)'; } elseif ($char === '&') { /* U+0026 AMPERSAND (&) Switch to the entity in attribute value state. */ $this->characterReferenceInAttributeValue("'"); } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-attribute-value-single-quote' )); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the current input character to the current attribute's value. Stay in the attribute value (single-quoted) state. */ $chars = $this->stream->charsUntil("'&"); $last = count($this->token['attr']) - 1; $this->token['attr'][$last]['value'] .= $char . $chars; $state = 'attribute value (single-quoted)'; } break; case 'attribute value (unquoted)': // Consume the next input character: $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Switch to the before attribute name state. */ $state = 'before attribute name'; } elseif ($char === '&') { /* U+0026 AMPERSAND (&) Switch to the entity in attribute value state, with the additional allowed character being U+003E GREATER-THAN SIGN (>). */ $this->characterReferenceInAttributeValue('>'); } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current tag token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-attribute-value-no-quotes' )); $this->stream->unget(); $state = 'data'; } else { /* U+0022 QUOTATION MARK (") U+0027 APOSTROPHE (') U+003C LESS-THAN SIGN (<) U+003D EQUALS SIGN (=) Parse error. Treat it as per the "anything else" entry below. */ if ($char === '"' || $char === "'" || $char === '=' || $char == '<') { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-character-in-unquoted-attribute-value' )); } /* Anything else Append the current input character to the current attribute's value. Stay in the attribute value (unquoted) state. */ $chars = $this->stream->charsUntil("\t\n\x0c &>\"'="); $last = count($this->token['attr']) - 1; $this->token['attr'][$last]['value'] .= $char . $chars; $state = 'attribute value (unquoted)'; } break; case 'after attribute value (quoted)': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Switch to the before attribute name state. */ $state = 'before attribute name'; } elseif ($char === '/') { /* U+002F SOLIDUS (/) Switch to the self-closing start tag state. */ $state = 'self-closing start tag'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current tag token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-EOF-after-attribute-value' )); $this->stream->unget(); $state = 'data'; } else { /* Anything else Parse error. Reconsume the character in the before attribute name state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-character-after-attribute-value' )); $this->stream->unget(); $state = 'before attribute name'; } break; case 'self-closing start tag': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Set the self-closing flag of the current tag token. Emit the current tag token. Switch to the data state. */ // not sure if this is the name we want $this->token['self-closing'] = true; $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-eof-after-self-closing' )); $this->stream->unget(); $state = 'data'; } else { /* Anything else Parse error. Reconsume the character in the before attribute name state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-character-after-self-closing' )); $this->stream->unget(); $state = 'before attribute name'; } break; case 'bogus comment': /* (This can only happen if the content model flag is set to the PCDATA state.) */ /* Consume every character up to the first U+003E GREATER-THAN SIGN character (>) or the end of the file (EOF), whichever comes first. Emit a comment token whose data is the concatenation of all the characters starting from and including the character that caused the state machine to switch into the bogus comment state, up to and including the last consumed character before the U+003E character, if any, or up to the end of the file otherwise. (If the comment was started by the end of the file (EOF), the token is empty.) */ $this->token['data'] .= (string) $this->stream->charsUntil('>'); $this->stream->char(); $this->emitToken($this->token); /* Switch to the data state. */ $state = 'data'; break; case 'markup declaration open': // Consume for below $hyphens = $this->stream->charsWhile('-', 2); if ($hyphens === '-') { $this->stream->unget(); } if ($hyphens !== '--') { $alpha = $this->stream->charsWhile(self::ALPHA, 7); } /* If the next two characters are both U+002D HYPHEN-MINUS (-) characters, consume those two characters, create a comment token whose data is the empty string, and switch to the comment state. */ if ($hyphens === '--') { $state = 'comment start'; $this->token = array( 'data' => '', 'type' => self::COMMENT ); /* Otherwise if the next seven characters are a case-insensitive match for the word "DOCTYPE", then consume those characters and switch to the DOCTYPE state. */ } elseif (strtoupper($alpha) === 'DOCTYPE') { $state = 'DOCTYPE'; // XXX not implemented /* Otherwise, if the insertion mode is "in foreign content" and the current node is not an element in the HTML namespace and the next seven characters are an ASCII case-sensitive match for the string "[CDATA[" (the five uppercase letters "CDATA" with a U+005B LEFT SQUARE BRACKET character before and after), then consume those characters and switch to the CDATA section state (which is unrelated to the content model flag's CDATA state). */ /* Otherwise, is is a parse error. Switch to the bogus comment state. The next character that is consumed, if any, is the first character that will be in the comment. */ } else { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-dashes-or-doctype' )); $this->token = array( 'data' => (string) $alpha, 'type' => self::COMMENT ); $state = 'bogus comment'; } break; case 'comment start': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '-') { /* U+002D HYPHEN-MINUS (-) Switch to the comment start dash state. */ $state = 'comment start dash'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Emit the comment token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'incorrect-comment' )); $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Emit the comment token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-comment' )); $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the input character to the comment token's data. Switch to the comment state. */ $this->token['data'] .= $char; $state = 'comment'; } break; case 'comment start dash': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '-') { /* U+002D HYPHEN-MINUS (-) Switch to the comment end state */ $state = 'comment end'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Emit the comment token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'incorrect-comment' )); $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* Parse error. Emit the comment token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-comment' )); $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { $this->token['data'] .= '-' . $char; $state = 'comment'; } break; case 'comment': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '-') { /* U+002D HYPHEN-MINUS (-) Switch to the comment end dash state */ $state = 'comment end dash'; } elseif ($char === false) { /* EOF Parse error. Emit the comment token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-comment' )); $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the input character to the comment token's data. Stay in the comment state. */ $chars = $this->stream->charsUntil('-'); $this->token['data'] .= $char . $chars; } break; case 'comment end dash': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '-') { /* U+002D HYPHEN-MINUS (-) Switch to the comment end state */ $state = 'comment end'; } elseif ($char === false) { /* EOF Parse error. Emit the comment token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-comment-end-dash' )); $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append a U+002D HYPHEN-MINUS (-) character and the input character to the comment token's data. Switch to the comment state. */ $this->token['data'] .= '-'.$char; $state = 'comment'; } break; case 'comment end': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the comment token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ($char === '-') { /* U+002D HYPHEN-MINUS (-) Parse error. Append a U+002D HYPHEN-MINUS (-) character to the comment token's data. Stay in the comment end state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-dash-after-double-dash-in-comment' )); $this->token['data'] .= '-'; } elseif ($char === "\t" || $char === "\n" || $char === "\x0a" || $char === ' ') { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-space-after-double-dash-in-comment' )); $this->token['data'] .= '--' . $char; $state = 'comment end space'; } elseif ($char === '!') { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-bang-after-double-dash-in-comment' )); $state = 'comment end bang'; } elseif ($char === false) { /* EOF Parse error. Emit the comment token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-comment-double-dash' )); $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Parse error. Append two U+002D HYPHEN-MINUS (-) characters and the input character to the comment token's data. Switch to the comment state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-char-in-comment' )); $this->token['data'] .= '--'.$char; $state = 'comment'; } break; case 'comment end bang': $char = $this->stream->char(); if ($char === '>') { $this->emitToken($this->token); $state = 'data'; } elseif ($char === "-") { $this->token['data'] .= '--!'; $state = 'comment end dash'; } elseif ($char === false) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-comment-end-bang' )); $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { $this->token['data'] .= '--!' . $char; $state = 'comment'; } break; case 'comment end space': $char = $this->stream->char(); if ($char === '>') { $this->emitToken($this->token); $state = 'data'; } elseif ($char === '-') { $state = 'comment end dash'; } elseif ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { $this->token['data'] .= $char; } elseif ($char === false) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-eof-in-comment-end-space', )); $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { $this->token['data'] .= $char; $state = 'comment'; } break; case 'DOCTYPE': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Switch to the before DOCTYPE name state. */ $state = 'before DOCTYPE name'; } elseif ($char === false) { /* EOF Parse error. Create a new DOCTYPE token. Set its force-quirks flag to on. Emit the token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'need-space-after-doctype-but-got-eof' )); $this->emitToken(array( 'name' => '', 'type' => self::DOCTYPE, 'force-quirks' => true, 'error' => true )); $this->stream->unget(); $state = 'data'; } else { /* Anything else Parse error. Reconsume the current character in the before DOCTYPE name state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'need-space-after-doctype' )); $this->stream->unget(); $state = 'before DOCTYPE name'; } break; case 'before DOCTYPE name': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the before DOCTYPE name state. */ } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Create a new DOCTYPE token. Set its force-quirks flag to on. Emit the token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-doctype-name-but-got-right-bracket' )); $this->emitToken(array( 'name' => '', 'type' => self::DOCTYPE, 'force-quirks' => true, 'error' => true )); $state = 'data'; } elseif ('A' <= $char && $char <= 'Z') { /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z Create a new DOCTYPE token. Set the token's name to the lowercase version of the input character (add 0x0020 to the character's code point). Switch to the DOCTYPE name state. */ $this->token = array( 'name' => strtolower($char), 'type' => self::DOCTYPE, 'error' => true ); $state = 'DOCTYPE name'; } elseif ($char === false) { /* EOF Parse error. Create a new DOCTYPE token. Set its force-quirks flag to on. Emit the token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-doctype-name-but-got-eof' )); $this->emitToken(array( 'name' => '', 'type' => self::DOCTYPE, 'force-quirks' => true, 'error' => true )); $this->stream->unget(); $state = 'data'; } else { /* Anything else Create a new DOCTYPE token. Set the token's name to the current input character. Switch to the DOCTYPE name state. */ $this->token = array( 'name' => $char, 'type' => self::DOCTYPE, 'error' => true ); $state = 'DOCTYPE name'; } break; case 'DOCTYPE name': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Switch to the after DOCTYPE name state. */ $state = 'after DOCTYPE name'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current DOCTYPE token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ('A' <= $char && $char <= 'Z') { /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z Append the lowercase version of the input character (add 0x0020 to the character's code point) to the current DOCTYPE token's name. Stay in the DOCTYPE name state. */ $this->token['name'] .= strtolower($char); } elseif ($char === false) { /* EOF Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype-name' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the current input character to the current DOCTYPE token's name. Stay in the DOCTYPE name state. */ $this->token['name'] .= $char; } // XXX this is probably some sort of quirks mode designation, // check tree-builder to be sure. In general 'error' needs // to be specc'ified, this probably means removing it at the end $this->token['error'] = ($this->token['name'] === 'HTML') ? false : true; break; case 'after DOCTYPE name': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the after DOCTYPE name state. */ } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current DOCTYPE token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else */ $nextSix = strtoupper($char . $this->stream->charsWhile(self::ALPHA, 5)); if ($nextSix === 'PUBLIC') { /* If the next six characters are an ASCII case-insensitive match for the word "PUBLIC", then consume those characters and switch to the before DOCTYPE public identifier state. */ $state = 'before DOCTYPE public identifier'; } elseif ($nextSix === 'SYSTEM') { /* Otherwise, if the next six characters are an ASCII case-insensitive match for the word "SYSTEM", then consume those characters and switch to the before DOCTYPE system identifier state. */ $state = 'before DOCTYPE system identifier'; } else { /* Otherwise, this is the parse error. Set the DOCTYPE token's force-quirks flag to on. Switch to the bogus DOCTYPE state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-space-or-right-bracket-in-doctype' )); $this->token['force-quirks'] = true; $this->token['error'] = true; $state = 'bogus DOCTYPE'; } } break; case 'before DOCTYPE public identifier': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the before DOCTYPE public identifier state. */ } elseif ($char === '"') { /* U+0022 QUOTATION MARK (") Set the DOCTYPE token's public identifier to the empty string (not missing), then switch to the DOCTYPE public identifier (double-quoted) state. */ $this->token['public'] = ''; $state = 'DOCTYPE public identifier (double-quoted)'; } elseif ($char === "'") { /* U+0027 APOSTROPHE (') Set the DOCTYPE token's public identifier to the empty string (not missing), then switch to the DOCTYPE public identifier (single-quoted) state. */ $this->token['public'] = ''; $state = 'DOCTYPE public identifier (single-quoted)'; } elseif ($char === '>') { /* Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-end-of-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Parse error. Set the DOCTYPE token's force-quirks flag to on. Switch to the bogus DOCTYPE state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-char-in-doctype' )); $this->token['force-quirks'] = true; $state = 'bogus DOCTYPE'; } break; case 'DOCTYPE public identifier (double-quoted)': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '"') { /* U+0022 QUOTATION MARK (") Switch to the after DOCTYPE public identifier state. */ $state = 'after DOCTYPE public identifier'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-end-of-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the current input character to the current DOCTYPE token's public identifier. Stay in the DOCTYPE public identifier (double-quoted) state. */ $this->token['public'] .= $char; } break; case 'DOCTYPE public identifier (single-quoted)': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "'") { /* U+0027 APOSTROPHE (') Switch to the after DOCTYPE public identifier state. */ $state = 'after DOCTYPE public identifier'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-end-of-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the current input character to the current DOCTYPE token's public identifier. Stay in the DOCTYPE public identifier (double-quoted) state. */ $this->token['public'] .= $char; } break; case 'after DOCTYPE public identifier': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the after DOCTYPE public identifier state. */ } elseif ($char === '"') { /* U+0022 QUOTATION MARK (") Set the DOCTYPE token's system identifier to the empty string (not missing), then switch to the DOCTYPE system identifier (double-quoted) state. */ $this->token['system'] = ''; $state = 'DOCTYPE system identifier (double-quoted)'; } elseif ($char === "'") { /* U+0027 APOSTROPHE (') Set the DOCTYPE token's system identifier to the empty string (not missing), then switch to the DOCTYPE system identifier (single-quoted) state. */ $this->token['system'] = ''; $state = 'DOCTYPE system identifier (single-quoted)'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current DOCTYPE token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Parse error. Set the DOCTYPE token's force-quirks flag to on. Switch to the bogus DOCTYPE state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-char-in-doctype' )); $this->token['force-quirks'] = true; $state = 'bogus DOCTYPE'; } break; case 'before DOCTYPE system identifier': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the before DOCTYPE system identifier state. */ } elseif ($char === '"') { /* U+0022 QUOTATION MARK (") Set the DOCTYPE token's system identifier to the empty string (not missing), then switch to the DOCTYPE system identifier (double-quoted) state. */ $this->token['system'] = ''; $state = 'DOCTYPE system identifier (double-quoted)'; } elseif ($char === "'") { /* U+0027 APOSTROPHE (') Set the DOCTYPE token's system identifier to the empty string (not missing), then switch to the DOCTYPE system identifier (single-quoted) state. */ $this->token['system'] = ''; $state = 'DOCTYPE system identifier (single-quoted)'; } elseif ($char === '>') { /* Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-char-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Parse error. Set the DOCTYPE token's force-quirks flag to on. Switch to the bogus DOCTYPE state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-char-in-doctype' )); $this->token['force-quirks'] = true; $state = 'bogus DOCTYPE'; } break; case 'DOCTYPE system identifier (double-quoted)': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '"') { /* U+0022 QUOTATION MARK (") Switch to the after DOCTYPE system identifier state. */ $state = 'after DOCTYPE system identifier'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-end-of-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the current input character to the current DOCTYPE token's system identifier. Stay in the DOCTYPE system identifier (double-quoted) state. */ $this->token['system'] .= $char; } break; case 'DOCTYPE system identifier (single-quoted)': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "'") { /* U+0027 APOSTROPHE (') Switch to the after DOCTYPE system identifier state. */ $state = 'after DOCTYPE system identifier'; } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Switch to the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-end-of-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Append the current input character to the current DOCTYPE token's system identifier. Stay in the DOCTYPE system identifier (double-quoted) state. */ $this->token['system'] .= $char; } break; case 'after DOCTYPE system identifier': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE Stay in the after DOCTYPE system identifier state. */ } elseif ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the current DOCTYPE token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* Parse error. Set the DOCTYPE token's force-quirks flag to on. Emit that DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'eof-in-doctype' )); $this->token['force-quirks'] = true; $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Parse error. Switch to the bogus DOCTYPE state. (This does not set the DOCTYPE token's force-quirks flag to on.) */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'unexpected-char-in-doctype' )); $state = 'bogus DOCTYPE'; } break; case 'bogus DOCTYPE': /* Consume the next input character: */ $char = $this->stream->char(); if ($char === '>') { /* U+003E GREATER-THAN SIGN (>) Emit the DOCTYPE token. Switch to the data state. */ $this->emitToken($this->token); $state = 'data'; } elseif ($char === false) { /* EOF Emit the DOCTYPE token. Reconsume the EOF character in the data state. */ $this->emitToken($this->token); $this->stream->unget(); $state = 'data'; } else { /* Anything else Stay in the bogus DOCTYPE state. */ } break; // case 'cdataSection': } } } /** * Returns a serialized representation of the tree. * * @return DOMDocument|DOMNodeList */ public function save() { return $this->tree->save(); } /** * @return HTML5_TreeBuilder The tree */ public function getTree() { return $this->tree; } /** * Returns the input stream. * * @return HTML5_InputStream */ public function stream() { return $this->stream; } /** * @param bool $allowed * @param bool $inattr * @return string */ private function consumeCharacterReference($allowed = false, $inattr = false) { // This goes quite far against spec, and is far closer to the Python // impl., mainly because we don't do the large unconsuming the spec // requires. // All consumed characters. $chars = $this->stream->char(); /* This section defines how to consume a character reference. This definition is used when parsing character references in text and in attributes. The behavior depends on the identity of the next character (the one immediately after the U+0026 AMPERSAND character): */ if ( $chars[0] === "\x09" || $chars[0] === "\x0A" || $chars[0] === "\x0C" || $chars[0] === "\x20" || $chars[0] === '<' || $chars[0] === '&' || $chars === false || $chars[0] === $allowed ) { /* U+0009 CHARACTER TABULATION U+000A LINE FEED (LF) U+000C FORM FEED (FF) U+0020 SPACE U+003C LESS-THAN SIGN U+0026 AMPERSAND EOF The additional allowed character, if there is one Not a character reference. No characters are consumed, and nothing is returned. (This is not an error, either.) */ // We already consumed, so unconsume. $this->stream->unget(); return '&'; } elseif ($chars[0] === '#') { /* Consume the U+0023 NUMBER SIGN. */ // Um, yeah, we already did that. /* The behavior further depends on the character after the U+0023 NUMBER SIGN: */ $chars .= $this->stream->char(); if (isset($chars[1]) && ($chars[1] === 'x' || $chars[1] === 'X')) { /* U+0078 LATIN SMALL LETTER X U+0058 LATIN CAPITAL LETTER X */ /* Consume the X. */ // Um, yeah, we already did that. /* Follow the steps below, but using the range of characters U+0030 DIGIT ZERO through to U+0039 DIGIT NINE, U+0061 LATIN SMALL LETTER A through to U+0066 LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER A, through to U+0046 LATIN CAPITAL LETTER F (in other words, 0123456789, ABCDEF, abcdef). */ $char_class = self::HEX; /* When it comes to interpreting the number, interpret it as a hexadecimal number. */ $hex = true; } else { /* Anything else */ // Unconsume because we shouldn't have consumed this. $chars = $chars[0]; $this->stream->unget(); /* Follow the steps below, but using the range of characters U+0030 DIGIT ZERO through to U+0039 DIGIT NINE (i.e. just 0123456789). */ $char_class = self::DIGIT; /* When it comes to interpreting the number, interpret it as a decimal number. */ $hex = false; } /* Consume as many characters as match the range of characters given above. */ $consumed = $this->stream->charsWhile($char_class); if ($consumed === '' || $consumed === false) { /* If no characters match the range, then don't consume any characters (and unconsume the U+0023 NUMBER SIGN character and, if appropriate, the X character). This is a parse error; nothing is returned. */ $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-numeric-entity' )); return '&' . $chars; } else { /* Otherwise, if the next character is a U+003B SEMICOLON, consume that too. If it isn't, there is a parse error. */ if ($this->stream->char() !== ';') { $this->stream->unget(); $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'numeric-entity-without-semicolon' )); } /* If one or more characters match the range, then take them all and interpret the string of characters as a number (either hexadecimal or decimal as appropriate). */ $codepoint = $hex ? hexdec($consumed) : (int) $consumed; /* If that number is one of the numbers in the first column of the following table, then this is a parse error. Find the row with that number in the first column, and return a character token for the Unicode character given in the second column of that row. */ $new_codepoint = HTML5_Data::getRealCodepoint($codepoint); if ($new_codepoint) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'illegal-windows-1252-entity' )); return HTML5_Data::utf8chr($new_codepoint); } else { /* Otherwise, if the number is greater than 0x10FFFF, then * this is a parse error. Return a U+FFFD REPLACEMENT * CHARACTER. */ if ($codepoint > 0x10FFFF) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'overlong-character-entity' // XXX probably not correct )); return "\xEF\xBF\xBD"; } /* Otherwise, return a character token for the Unicode * character whose code point is that number. If the * number is in the range 0x0001 to 0x0008, 0x000E to * 0x001F, 0x007F to 0x009F, 0xD800 to 0xDFFF, 0xFDD0 to * 0xFDEF, or is one of 0x000B, 0xFFFE, 0xFFFF, 0x1FFFE, * 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE, * 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, * 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, * 0xAFFFF, 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, * 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, * or 0x10FFFF, then this is a parse error. */ // && has higher precedence than || if ( $codepoint >= 0x0000 && $codepoint <= 0x0008 || $codepoint === 0x000B || $codepoint >= 0x000E && $codepoint <= 0x001F || $codepoint >= 0x007F && $codepoint <= 0x009F || $codepoint >= 0xD800 && $codepoint <= 0xDFFF || $codepoint >= 0xFDD0 && $codepoint <= 0xFDEF || ($codepoint & 0xFFFE) === 0xFFFE || $codepoint == 0x10FFFF || $codepoint == 0x10FFFE ) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'illegal-codepoint-for-numeric-entity' )); } return HTML5_Data::utf8chr($codepoint); } } } else { /* Anything else */ /* Consume the maximum number of characters possible, with the consumed characters matching one of the identifiers in the first column of the named character references table (in a case-sensitive manner). */ // What we actually do here is consume as much as we can while it // matches the start of one of the identifiers in the first column. $refs = HTML5_Data::getNamedCharacterReferences(); // Get the longest string which is the start of an identifier // ($chars) as well as the longest identifier which matches ($id) // and its codepoint ($codepoint). $codepoint = false; $char = $chars; while ($char !== false && isset($refs[$char])) { $refs = $refs[$char]; if (isset($refs['codepoint'])) { $id = $chars; $codepoint = $refs['codepoint']; } $chars .= $char = $this->stream->char(); } // Unconsume the one character we just took which caused the while // statement to fail. This could be anything and could cause state // changes (as if it matches the while loop it must be // alphanumeric so we can just concat it to whatever we get later). $this->stream->unget(); if ($char !== false) { $chars = substr($chars, 0, -1); } /* If no match can be made, then this is a parse error. No characters are consumed, and nothing is returned. */ if (!$codepoint) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'expected-named-entity' )); return '&' . $chars; } /* If the last character matched is not a U+003B SEMICOLON (;), there is a parse error. */ $semicolon = true; if (substr($id, -1) !== ';') { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'named-entity-without-semicolon' )); $semicolon = false; } /* If the character reference is being consumed as part of an attribute, and the last character matched is not a U+003B SEMICOLON (;), and the next character is in the range U+0030 DIGIT ZERO to U+0039 DIGIT NINE, U+0041 LATIN CAPITAL LETTER A to U+005A LATIN CAPITAL LETTER Z, or U+0061 LATIN SMALL LETTER A to U+007A LATIN SMALL LETTER Z, then, for historical reasons, all the characters that were matched after the U+0026 AMPERSAND (&) must be unconsumed, and nothing is returned. */ if ($inattr && !$semicolon) { // The next character is either the next character in $chars or in the stream. if (strlen($chars) > strlen($id)) { $next = substr($chars, strlen($id), 1); } else { $next = $this->stream->char(); $this->stream->unget(); } if ( '0' <= $next && $next <= '9' || 'A' <= $next && $next <= 'Z' || 'a' <= $next && $next <= 'z' ) { return '&' . $chars; } } /* Otherwise, return a character token for the character corresponding to the character reference name (as given by the second column of the named character references table). */ return HTML5_Data::utf8chr($codepoint) . substr($chars, strlen($id)); } } /** * @param bool $allowed */ private function characterReferenceInAttributeValue($allowed = false) { /* Attempt to consume a character reference. */ $entity = $this->consumeCharacterReference($allowed, true); /* If nothing is returned, append a U+0026 AMPERSAND character to the current attribute's value. Otherwise, append the returned character token to the current attribute's value. */ $char = (!$entity) ? '&' : $entity; $last = count($this->token['attr']) - 1; $this->token['attr'][$last]['value'] .= $char; /* Finally, switch back to the attribute value state that you were in when were switched into this state. */ } /** * Emits a token, passing it on to the tree builder. * * @param $token * @param bool $checkStream * @param bool $dry */ protected function emitToken($token, $checkStream = true, $dry = false) { if ($checkStream === true) { // Emit errors from input stream. while ($this->stream->errors) { $this->emitToken(array_shift($this->stream->errors), false); } } if ($token['type'] === self::ENDTAG && !empty($token['attr'])) { for ($i = 0; $i < count($token['attr']); $i++) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'attributes-in-end-tag' )); } } if ($token['type'] === self::ENDTAG && !empty($token['self-closing'])) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'self-closing-flag-on-end-tag', )); } if ($token['type'] === self::STARTTAG) { // This could be changed to actually pass the tree-builder a hash $hash = array(); foreach ($token['attr'] as $keypair) { if (isset($hash[$keypair['name']])) { $this->emitToken(array( 'type' => self::PARSEERROR, 'data' => 'duplicate-attribute', )); } else { $hash[$keypair['name']] = $keypair['value']; } } } if ($dry === false) { // the current structure of attributes is not a terribly good one $this->tree->emitToken($token); } if ($dry === false && is_int($this->tree->content_model)) { $this->content_model = $this->tree->content_model; $this->tree->content_model = null; } elseif ($token['type'] === self::ENDTAG) { $this->content_model = self::PCDATA; } } } dompdf/lib/html5lib/Parser.php 0000604 00000002264 15173213355 0012255 0 ustar 00 <?php require_once dirname(__FILE__) . '/Data.php'; require_once dirname(__FILE__) . '/InputStream.php'; require_once dirname(__FILE__) . '/TreeBuilder.php'; require_once dirname(__FILE__) . '/Tokenizer.php'; /** * Outwards facing interface for HTML5. */ class HTML5_Parser { /** * Parses a full HTML document. * @param $text | HTML text to parse * @param $builder | Custom builder implementation * @return DOMDocument|DOMNodeList Parsed HTML as DOMDocument */ static public function parse($text, $builder = null) { $tokenizer = new HTML5_Tokenizer($text, $builder); $tokenizer->parse(); return $tokenizer->save(); } /** * Parses an HTML fragment. * @param $text | HTML text to parse * @param $context String name of context element to pretend parsing is in. * @param $builder | Custom builder implementation * @return DOMDocument|DOMNodeList Parsed HTML as DOMDocument */ static public function parseFragment($text, $context = null, $builder = null) { $tokenizer = new HTML5_Tokenizer($text, $builder); $tokenizer->parseFragment($context); return $tokenizer->save(); } } dompdf/lib/fonts/Times-Italic.afm 0000604 00000201652 15173213355 0012674 0 ustar 00 StartFontMetrics 4.1 Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. Comment Creation Date: Thu May 1 12:56:55 1997 Comment UniqueID 43067 Comment VMusage 47727 58752 FontName Times-Italic FullName Times Italic FamilyName Times Weight Medium ItalicAngle -15.5 IsFixedPitch false CharacterSet ExtendedRoman FontBBox -169 -217 1010 883 UnderlinePosition -100 UnderlineThickness 50 Version 002.000 Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. EncodingScheme WinAnsiEncoding CapHeight 653 XHeight 441 Ascender 683 Descender -217 StdHW 32 StdVW 76 StartCharMetrics 317 C 32 ; WX 250 ; N space ; B 0 0 0 0 ; C 160 ; WX 250 ; N space ; B 0 0 0 0 ; C 33 ; WX 333 ; N exclam ; B 39 -11 302 667 ; C 34 ; WX 420 ; N quotedbl ; B 144 421 432 666 ; C 35 ; WX 500 ; N numbersign ; B 2 0 540 676 ; C 36 ; WX 500 ; N dollar ; B 31 -89 497 731 ; C 37 ; WX 833 ; N percent ; B 79 -13 790 676 ; C 38 ; WX 778 ; N ampersand ; B 76 -18 723 666 ; C 146 ; WX 333 ; N quoteright ; B 151 436 290 666 ; C 40 ; WX 333 ; N parenleft ; B 42 -181 315 669 ; C 41 ; WX 333 ; N parenright ; B 16 -180 289 669 ; C 42 ; WX 500 ; N asterisk ; B 128 255 492 666 ; C 43 ; WX 675 ; N plus ; B 86 0 590 506 ; C 44 ; WX 250 ; N comma ; B -4 -129 135 101 ; C 45 ; WX 333 ; N hyphen ; B 49 192 282 255 ; C 173 ; WX 333 ; N hyphen ; B 49 192 282 255 ; C 46 ; WX 250 ; N period ; B 27 -11 138 100 ; C 47 ; WX 278 ; N slash ; B -65 -18 386 666 ; C 48 ; WX 500 ; N zero ; B 32 -7 497 676 ; C 49 ; WX 500 ; N one ; B 49 0 409 676 ; C 50 ; WX 500 ; N two ; B 12 0 452 676 ; C 51 ; WX 500 ; N three ; B 15 -7 465 676 ; C 52 ; WX 500 ; N four ; B 1 0 479 676 ; C 53 ; WX 500 ; N five ; B 15 -7 491 666 ; C 54 ; WX 500 ; N six ; B 30 -7 521 686 ; C 55 ; WX 500 ; N seven ; B 75 -8 537 666 ; C 56 ; WX 500 ; N eight ; B 30 -7 493 676 ; C 57 ; WX 500 ; N nine ; B 23 -17 492 676 ; C 58 ; WX 333 ; N colon ; B 50 -11 261 441 ; C 59 ; WX 333 ; N semicolon ; B 27 -129 261 441 ; C 60 ; WX 675 ; N less ; B 84 -8 592 514 ; C 61 ; WX 675 ; N equal ; B 86 120 590 386 ; C 62 ; WX 675 ; N greater ; B 84 -8 592 514 ; C 63 ; WX 500 ; N question ; B 132 -12 472 664 ; C 64 ; WX 920 ; N at ; B 118 -18 806 666 ; C 65 ; WX 611 ; N A ; B -51 0 564 668 ; C 66 ; WX 611 ; N B ; B -8 0 588 653 ; C 67 ; WX 667 ; N C ; B 66 -18 689 666 ; C 68 ; WX 722 ; N D ; B -8 0 700 653 ; C 69 ; WX 611 ; N E ; B -1 0 634 653 ; C 70 ; WX 611 ; N F ; B 8 0 645 653 ; C 71 ; WX 722 ; N G ; B 52 -18 722 666 ; C 72 ; WX 722 ; N H ; B -8 0 767 653 ; C 73 ; WX 333 ; N I ; B -8 0 384 653 ; C 74 ; WX 444 ; N J ; B -6 -18 491 653 ; C 75 ; WX 667 ; N K ; B 7 0 722 653 ; C 76 ; WX 556 ; N L ; B -8 0 559 653 ; C 77 ; WX 833 ; N M ; B -18 0 873 653 ; C 78 ; WX 667 ; N N ; B -20 -15 727 653 ; C 79 ; WX 722 ; N O ; B 60 -18 699 666 ; C 80 ; WX 611 ; N P ; B 0 0 605 653 ; C 81 ; WX 722 ; N Q ; B 59 -182 699 666 ; C 82 ; WX 611 ; N R ; B -13 0 588 653 ; C 83 ; WX 500 ; N S ; B 17 -18 508 667 ; C 84 ; WX 556 ; N T ; B 59 0 633 653 ; C 85 ; WX 722 ; N U ; B 102 -18 765 653 ; C 86 ; WX 611 ; N V ; B 76 -18 688 653 ; C 87 ; WX 833 ; N W ; B 71 -18 906 653 ; C 88 ; WX 611 ; N X ; B -29 0 655 653 ; C 89 ; WX 556 ; N Y ; B 78 0 633 653 ; C 90 ; WX 556 ; N Z ; B -6 0 606 653 ; C 91 ; WX 389 ; N bracketleft ; B 21 -153 391 663 ; C 92 ; WX 278 ; N backslash ; B -41 -18 319 666 ; C 93 ; WX 389 ; N bracketright ; B 12 -153 382 663 ; C 94 ; WX 422 ; N asciicircum ; B 0 301 422 666 ; C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; C 145 ; WX 333 ; N quoteleft ; B 171 436 310 666 ; C 97 ; WX 500 ; N a ; B 17 -11 476 441 ; C 98 ; WX 500 ; N b ; B 23 -11 473 683 ; C 99 ; WX 444 ; N c ; B 30 -11 425 441 ; C 100 ; WX 500 ; N d ; B 15 -13 527 683 ; C 101 ; WX 444 ; N e ; B 31 -11 412 441 ; C 102 ; WX 278 ; N f ; B -147 -207 424 678 ; L i fi ; L l fl ; C 103 ; WX 500 ; N g ; B 8 -206 472 441 ; C 104 ; WX 500 ; N h ; B 19 -9 478 683 ; C 105 ; WX 278 ; N i ; B 49 -11 264 654 ; C 106 ; WX 278 ; N j ; B -124 -207 276 654 ; C 107 ; WX 444 ; N k ; B 14 -11 461 683 ; C 108 ; WX 278 ; N l ; B 41 -11 279 683 ; C 109 ; WX 722 ; N m ; B 12 -9 704 441 ; C 110 ; WX 500 ; N n ; B 14 -9 474 441 ; C 111 ; WX 500 ; N o ; B 27 -11 468 441 ; C 112 ; WX 500 ; N p ; B -75 -205 469 441 ; C 113 ; WX 500 ; N q ; B 25 -209 483 441 ; C 114 ; WX 389 ; N r ; B 45 0 412 441 ; C 115 ; WX 389 ; N s ; B 16 -13 366 442 ; C 116 ; WX 278 ; N t ; B 37 -11 296 546 ; C 117 ; WX 500 ; N u ; B 42 -11 475 441 ; C 118 ; WX 444 ; N v ; B 21 -18 426 441 ; C 119 ; WX 667 ; N w ; B 16 -18 648 441 ; C 120 ; WX 444 ; N x ; B -27 -11 447 441 ; C 121 ; WX 444 ; N y ; B -24 -206 426 441 ; C 122 ; WX 389 ; N z ; B -2 -81 380 428 ; C 123 ; WX 400 ; N braceleft ; B 51 -177 407 687 ; C 124 ; WX 275 ; N bar ; B 105 -217 171 783 ; C 125 ; WX 400 ; N braceright ; B -7 -177 349 687 ; C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ; C 161 ; WX 389 ; N exclamdown ; B 59 -205 322 473 ; C 162 ; WX 500 ; N cent ; B 77 -143 472 560 ; C 163 ; WX 500 ; N sterling ; B 10 -6 517 670 ; C -1 ; WX 167 ; N fraction ; B -169 -10 337 676 ; C 165 ; WX 500 ; N yen ; B 27 0 603 653 ; C 131 ; WX 500 ; N florin ; B 25 -182 507 682 ; C 167 ; WX 500 ; N section ; B 53 -162 461 666 ; C 164 ; WX 500 ; N currency ; B -22 53 522 597 ; C 39 ; WX 214 ; N quotesingle ; B 132 421 241 666 ; C 147 ; WX 556 ; N quotedblleft ; B 166 436 514 666 ; C 171 ; WX 500 ; N guillemotleft ; B 53 37 445 403 ; C 139 ; WX 333 ; N guilsinglleft ; B 51 37 281 403 ; C 155 ; WX 333 ; N guilsinglright ; B 52 37 282 403 ; C -1 ; WX 500 ; N fi ; B -141 -207 481 681 ; C -1 ; WX 500 ; N fl ; B -141 -204 518 682 ; C 150 ; WX 500 ; N endash ; B -6 197 505 243 ; C 134 ; WX 500 ; N dagger ; B 101 -159 488 666 ; C 135 ; WX 500 ; N daggerdbl ; B 22 -143 491 666 ; C 183 ; WX 250 ; N periodcentered ; B 70 199 181 310 ; C 182 ; WX 523 ; N paragraph ; B 55 -123 616 653 ; C 149 ; WX 350 ; N bullet ; B 40 191 310 461 ; C 130 ; WX 333 ; N quotesinglbase ; B 44 -129 183 101 ; C 132 ; WX 556 ; N quotedblbase ; B 57 -129 405 101 ; C 148 ; WX 556 ; N quotedblright ; B 151 436 499 666 ; C 187 ; WX 500 ; N guillemotright ; B 55 37 447 403 ; C 133 ; WX 889 ; N ellipsis ; B 57 -11 762 100 ; C 137 ; WX 1000 ; N perthousand ; B 25 -19 1010 706 ; C 191 ; WX 500 ; N questiondown ; B 28 -205 368 471 ; C 96 ; WX 333 ; N grave ; B 121 492 311 664 ; C 180 ; WX 333 ; N acute ; B 180 494 403 664 ; C 136 ; WX 333 ; N circumflex ; B 91 492 385 661 ; C 152 ; WX 333 ; N tilde ; B 100 517 427 624 ; C 175 ; WX 333 ; N macron ; B 99 532 411 583 ; C -1 ; WX 333 ; N breve ; B 117 492 418 650 ; C -1 ; WX 333 ; N dotaccent ; B 207 548 305 646 ; C 168 ; WX 333 ; N dieresis ; B 107 548 405 646 ; C -1 ; WX 333 ; N ring ; B 155 492 355 691 ; C 184 ; WX 333 ; N cedilla ; B -30 -217 182 0 ; C -1 ; WX 333 ; N hungarumlaut ; B 93 494 486 664 ; C -1 ; WX 333 ; N ogonek ; B 20 -169 203 40 ; C -1 ; WX 333 ; N caron ; B 121 492 426 661 ; C 151 ; WX 889 ; N emdash ; B -6 197 894 243 ; C 198 ; WX 889 ; N AE ; B -27 0 911 653 ; C 170 ; WX 276 ; N ordfeminine ; B 42 406 352 676 ; C -1 ; WX 556 ; N Lslash ; B -8 0 559 653 ; C 216 ; WX 722 ; N Oslash ; B 60 -105 699 722 ; C 140 ; WX 944 ; N OE ; B 49 -8 964 666 ; C 186 ; WX 310 ; N ordmasculine ; B 67 406 362 676 ; C 230 ; WX 667 ; N ae ; B 23 -11 640 441 ; C -1 ; WX 278 ; N dotlessi ; B 49 -11 235 441 ; C -1 ; WX 278 ; N lslash ; B 41 -11 312 683 ; C 248 ; WX 500 ; N oslash ; B 28 -135 469 554 ; C 156 ; WX 667 ; N oe ; B 20 -12 646 441 ; C 223 ; WX 500 ; N germandbls ; B -168 -207 493 679 ; C 207 ; WX 333 ; N Idieresis ; B -8 0 435 818 ; C 233 ; WX 444 ; N eacute ; B 31 -11 459 664 ; C -1 ; WX 500 ; N abreve ; B 17 -11 502 650 ; C -1 ; WX 500 ; N uhungarumlaut ; B 42 -11 580 664 ; C -1 ; WX 444 ; N ecaron ; B 31 -11 482 661 ; C 159 ; WX 556 ; N Ydieresis ; B 78 0 633 818 ; C 247 ; WX 675 ; N divide ; B 86 -11 590 517 ; C 221 ; WX 556 ; N Yacute ; B 78 0 633 876 ; C 194 ; WX 611 ; N Acircumflex ; B -51 0 564 873 ; C 225 ; WX 500 ; N aacute ; B 17 -11 487 664 ; C 219 ; WX 722 ; N Ucircumflex ; B 102 -18 765 873 ; C 253 ; WX 444 ; N yacute ; B -24 -206 459 664 ; C -1 ; WX 389 ; N scommaaccent ; B 16 -217 366 442 ; C 234 ; WX 444 ; N ecircumflex ; B 31 -11 441 661 ; C -1 ; WX 722 ; N Uring ; B 102 -18 765 883 ; C 220 ; WX 722 ; N Udieresis ; B 102 -18 765 818 ; C -1 ; WX 500 ; N aogonek ; B 17 -169 476 441 ; C 218 ; WX 722 ; N Uacute ; B 102 -18 765 876 ; C -1 ; WX 500 ; N uogonek ; B 42 -169 477 441 ; C 203 ; WX 611 ; N Edieresis ; B -1 0 634 818 ; C -1 ; WX 722 ; N Dcroat ; B -8 0 700 653 ; C -1 ; WX 250 ; N commaaccent ; B 8 -217 133 -50 ; C 169 ; WX 760 ; N copyright ; B 41 -18 719 666 ; C -1 ; WX 611 ; N Emacron ; B -1 0 634 795 ; C -1 ; WX 444 ; N ccaron ; B 30 -11 482 661 ; C 229 ; WX 500 ; N aring ; B 17 -11 476 691 ; C -1 ; WX 667 ; N Ncommaaccent ; B -20 -187 727 653 ; C -1 ; WX 278 ; N lacute ; B 41 -11 395 876 ; C 224 ; WX 500 ; N agrave ; B 17 -11 476 664 ; C -1 ; WX 556 ; N Tcommaaccent ; B 59 -217 633 653 ; C -1 ; WX 667 ; N Cacute ; B 66 -18 690 876 ; C 227 ; WX 500 ; N atilde ; B 17 -11 511 624 ; C -1 ; WX 611 ; N Edotaccent ; B -1 0 634 818 ; C 154 ; WX 389 ; N scaron ; B 16 -13 454 661 ; C -1 ; WX 389 ; N scedilla ; B 16 -217 366 442 ; C 237 ; WX 278 ; N iacute ; B 49 -11 355 664 ; C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ; C -1 ; WX 611 ; N Rcaron ; B -13 0 588 873 ; C -1 ; WX 722 ; N Gcommaaccent ; B 52 -217 722 666 ; C 251 ; WX 500 ; N ucircumflex ; B 42 -11 475 661 ; C 226 ; WX 500 ; N acircumflex ; B 17 -11 476 661 ; C -1 ; WX 611 ; N Amacron ; B -51 0 564 795 ; C -1 ; WX 389 ; N rcaron ; B 45 0 434 661 ; C 231 ; WX 444 ; N ccedilla ; B 30 -217 425 441 ; C -1 ; WX 556 ; N Zdotaccent ; B -6 0 606 818 ; C 222 ; WX 611 ; N Thorn ; B 0 0 569 653 ; C -1 ; WX 722 ; N Omacron ; B 60 -18 699 795 ; C -1 ; WX 611 ; N Racute ; B -13 0 588 876 ; C -1 ; WX 500 ; N Sacute ; B 17 -18 508 876 ; C -1 ; WX 544 ; N dcaron ; B 15 -13 658 683 ; C -1 ; WX 722 ; N Umacron ; B 102 -18 765 795 ; C -1 ; WX 500 ; N uring ; B 42 -11 475 691 ; C 179 ; WX 300 ; N threesuperior ; B 43 268 339 676 ; C 210 ; WX 722 ; N Ograve ; B 60 -18 699 876 ; C 192 ; WX 611 ; N Agrave ; B -51 0 564 876 ; C -1 ; WX 611 ; N Abreve ; B -51 0 564 862 ; C 215 ; WX 675 ; N multiply ; B 93 8 582 497 ; C 250 ; WX 500 ; N uacute ; B 42 -11 477 664 ; C -1 ; WX 556 ; N Tcaron ; B 59 0 633 873 ; C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ; C 255 ; WX 444 ; N ydieresis ; B -24 -206 441 606 ; C -1 ; WX 667 ; N Nacute ; B -20 -15 727 876 ; C 238 ; WX 278 ; N icircumflex ; B 33 -11 327 661 ; C 202 ; WX 611 ; N Ecircumflex ; B -1 0 634 873 ; C 228 ; WX 500 ; N adieresis ; B 17 -11 489 606 ; C 235 ; WX 444 ; N edieresis ; B 31 -11 451 606 ; C -1 ; WX 444 ; N cacute ; B 30 -11 459 664 ; C -1 ; WX 500 ; N nacute ; B 14 -9 477 664 ; C -1 ; WX 500 ; N umacron ; B 42 -11 485 583 ; C -1 ; WX 667 ; N Ncaron ; B -20 -15 727 873 ; C 205 ; WX 333 ; N Iacute ; B -8 0 433 876 ; C 177 ; WX 675 ; N plusminus ; B 86 0 590 506 ; C 166 ; WX 275 ; N brokenbar ; B 105 -142 171 708 ; C 174 ; WX 760 ; N registered ; B 41 -18 719 666 ; C -1 ; WX 722 ; N Gbreve ; B 52 -18 722 862 ; C -1 ; WX 333 ; N Idotaccent ; B -8 0 384 818 ; C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ; C 200 ; WX 611 ; N Egrave ; B -1 0 634 876 ; C -1 ; WX 389 ; N racute ; B 45 0 431 664 ; C -1 ; WX 500 ; N omacron ; B 27 -11 495 583 ; C -1 ; WX 556 ; N Zacute ; B -6 0 606 876 ; C 142 ; WX 556 ; N Zcaron ; B -6 0 606 873 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 523 658 ; C 208 ; WX 722 ; N Eth ; B -8 0 700 653 ; C 199 ; WX 667 ; N Ccedilla ; B 66 -217 689 666 ; C -1 ; WX 278 ; N lcommaaccent ; B 22 -217 279 683 ; C -1 ; WX 300 ; N tcaron ; B 37 -11 407 681 ; C -1 ; WX 444 ; N eogonek ; B 31 -169 412 441 ; C -1 ; WX 722 ; N Uogonek ; B 102 -184 765 653 ; C 193 ; WX 611 ; N Aacute ; B -51 0 564 876 ; C 196 ; WX 611 ; N Adieresis ; B -51 0 564 818 ; C 232 ; WX 444 ; N egrave ; B 31 -11 412 664 ; C -1 ; WX 389 ; N zacute ; B -2 -81 431 664 ; C -1 ; WX 278 ; N iogonek ; B 49 -169 264 654 ; C 211 ; WX 722 ; N Oacute ; B 60 -18 699 876 ; C 243 ; WX 500 ; N oacute ; B 27 -11 487 664 ; C -1 ; WX 500 ; N amacron ; B 17 -11 495 583 ; C -1 ; WX 389 ; N sacute ; B 16 -13 431 664 ; C 239 ; WX 278 ; N idieresis ; B 49 -11 352 606 ; C 212 ; WX 722 ; N Ocircumflex ; B 60 -18 699 873 ; C 217 ; WX 722 ; N Ugrave ; B 102 -18 765 876 ; C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; C 254 ; WX 500 ; N thorn ; B -75 -205 469 683 ; C 178 ; WX 300 ; N twosuperior ; B 33 271 324 676 ; C 214 ; WX 722 ; N Odieresis ; B 60 -18 699 818 ; C 181 ; WX 500 ; N mu ; B -30 -209 497 428 ; C 236 ; WX 278 ; N igrave ; B 49 -11 284 664 ; C -1 ; WX 500 ; N ohungarumlaut ; B 27 -11 590 664 ; C -1 ; WX 611 ; N Eogonek ; B -1 -169 634 653 ; C -1 ; WX 500 ; N dcroat ; B 15 -13 572 683 ; C 190 ; WX 750 ; N threequarters ; B 23 -10 736 676 ; C -1 ; WX 500 ; N Scedilla ; B 17 -217 508 667 ; C -1 ; WX 300 ; N lcaron ; B 41 -11 407 683 ; C -1 ; WX 667 ; N Kcommaaccent ; B 7 -217 722 653 ; C -1 ; WX 556 ; N Lacute ; B -8 0 559 876 ; C 153 ; WX 980 ; N trademark ; B 30 247 957 653 ; C -1 ; WX 444 ; N edotaccent ; B 31 -11 412 606 ; C 204 ; WX 333 ; N Igrave ; B -8 0 384 876 ; C -1 ; WX 333 ; N Imacron ; B -8 0 441 795 ; C -1 ; WX 611 ; N Lcaron ; B -8 0 586 653 ; C 189 ; WX 750 ; N onehalf ; B 34 -10 749 676 ; C -1 ; WX 549 ; N lessequal ; B 26 0 523 658 ; C 244 ; WX 500 ; N ocircumflex ; B 27 -11 468 661 ; C 241 ; WX 500 ; N ntilde ; B 14 -9 476 624 ; C -1 ; WX 722 ; N Uhungarumlaut ; B 102 -18 765 876 ; C 201 ; WX 611 ; N Eacute ; B -1 0 634 876 ; C -1 ; WX 444 ; N emacron ; B 31 -11 457 583 ; C -1 ; WX 500 ; N gbreve ; B 8 -206 487 650 ; C 188 ; WX 750 ; N onequarter ; B 33 -10 736 676 ; C 138 ; WX 500 ; N Scaron ; B 17 -18 520 873 ; C -1 ; WX 500 ; N Scommaaccent ; B 17 -217 508 667 ; C -1 ; WX 722 ; N Ohungarumlaut ; B 60 -18 699 876 ; C 176 ; WX 400 ; N degree ; B 101 390 387 676 ; C 242 ; WX 500 ; N ograve ; B 27 -11 468 664 ; C -1 ; WX 667 ; N Ccaron ; B 66 -18 689 873 ; C 249 ; WX 500 ; N ugrave ; B 42 -11 475 664 ; C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ; C -1 ; WX 722 ; N Dcaron ; B -8 0 700 873 ; C -1 ; WX 389 ; N rcommaaccent ; B -3 -217 412 441 ; C 209 ; WX 667 ; N Ntilde ; B -20 -15 727 836 ; C 245 ; WX 500 ; N otilde ; B 27 -11 496 624 ; C -1 ; WX 611 ; N Rcommaaccent ; B -13 -187 588 653 ; C -1 ; WX 556 ; N Lcommaaccent ; B -8 -217 559 653 ; C 195 ; WX 611 ; N Atilde ; B -51 0 566 836 ; C -1 ; WX 611 ; N Aogonek ; B -51 -169 566 668 ; C 197 ; WX 611 ; N Aring ; B -51 0 564 883 ; C 213 ; WX 722 ; N Otilde ; B 60 -18 699 836 ; C -1 ; WX 389 ; N zdotaccent ; B -2 -81 380 606 ; C -1 ; WX 611 ; N Ecaron ; B -1 0 634 873 ; C -1 ; WX 333 ; N Iogonek ; B -8 -169 384 653 ; C -1 ; WX 444 ; N kcommaaccent ; B 14 -187 461 683 ; C -1 ; WX 675 ; N minus ; B 86 220 590 286 ; C 206 ; WX 333 ; N Icircumflex ; B -8 0 425 873 ; C -1 ; WX 500 ; N ncaron ; B 14 -9 510 661 ; C -1 ; WX 278 ; N tcommaaccent ; B 2 -217 296 546 ; C 172 ; WX 675 ; N logicalnot ; B 86 108 590 386 ; C 246 ; WX 500 ; N odieresis ; B 27 -11 489 606 ; C 252 ; WX 500 ; N udieresis ; B 42 -11 479 606 ; C -1 ; WX 549 ; N notequal ; B 12 -29 537 541 ; C -1 ; WX 500 ; N gcommaaccent ; B 8 -206 472 706 ; C 240 ; WX 500 ; N eth ; B 27 -11 482 683 ; C 158 ; WX 389 ; N zcaron ; B -2 -81 434 661 ; C -1 ; WX 500 ; N ncommaaccent ; B 14 -187 474 441 ; C 185 ; WX 300 ; N onesuperior ; B 43 271 284 676 ; C -1 ; WX 278 ; N imacron ; B 46 -11 311 583 ; C 128 ; WX 500 ; N Euro ; B 0 0 0 0 ; EndCharMetrics StartKernData StartKernPairs 2321 KPX A C -30 KPX A Cacute -30 KPX A Ccaron -30 KPX A Ccedilla -30 KPX A G -35 KPX A Gbreve -35 KPX A Gcommaaccent -35 KPX A O -40 KPX A Oacute -40 KPX A Ocircumflex -40 KPX A Odieresis -40 KPX A Ograve -40 KPX A Ohungarumlaut -40 KPX A Omacron -40 KPX A Oslash -40 KPX A Otilde -40 KPX A Q -40 KPX A T -37 KPX A Tcaron -37 KPX A Tcommaaccent -37 KPX A U -50 KPX A Uacute -50 KPX A Ucircumflex -50 KPX A Udieresis -50 KPX A Ugrave -50 KPX A Uhungarumlaut -50 KPX A Umacron -50 KPX A Uogonek -50 KPX A Uring -50 KPX A V -105 KPX A W -95 KPX A Y -55 KPX A Yacute -55 KPX A Ydieresis -55 KPX A quoteright -37 KPX A u -20 KPX A uacute -20 KPX A ucircumflex -20 KPX A udieresis -20 KPX A ugrave -20 KPX A uhungarumlaut -20 KPX A umacron -20 KPX A uogonek -20 KPX A uring -20 KPX A v -55 KPX A w -55 KPX A y -55 KPX A yacute -55 KPX A ydieresis -55 KPX Aacute C -30 KPX Aacute Cacute -30 KPX Aacute Ccaron -30 KPX Aacute Ccedilla -30 KPX Aacute G -35 KPX Aacute Gbreve -35 KPX Aacute Gcommaaccent -35 KPX Aacute O -40 KPX Aacute Oacute -40 KPX Aacute Ocircumflex -40 KPX Aacute Odieresis -40 KPX Aacute Ograve -40 KPX Aacute Ohungarumlaut -40 KPX Aacute Omacron -40 KPX Aacute Oslash -40 KPX Aacute Otilde -40 KPX Aacute Q -40 KPX Aacute T -37 KPX Aacute Tcaron -37 KPX Aacute Tcommaaccent -37 KPX Aacute U -50 KPX Aacute Uacute -50 KPX Aacute Ucircumflex -50 KPX Aacute Udieresis -50 KPX Aacute Ugrave -50 KPX Aacute Uhungarumlaut -50 KPX Aacute Umacron -50 KPX Aacute Uogonek -50 KPX Aacute Uring -50 KPX Aacute V -105 KPX Aacute W -95 KPX Aacute Y -55 KPX Aacute Yacute -55 KPX Aacute Ydieresis -55 KPX Aacute quoteright -37 KPX Aacute u -20 KPX Aacute uacute -20 KPX Aacute ucircumflex -20 KPX Aacute udieresis -20 KPX Aacute ugrave -20 KPX Aacute uhungarumlaut -20 KPX Aacute umacron -20 KPX Aacute uogonek -20 KPX Aacute uring -20 KPX Aacute v -55 KPX Aacute w -55 KPX Aacute y -55 KPX Aacute yacute -55 KPX Aacute ydieresis -55 KPX Abreve C -30 KPX Abreve Cacute -30 KPX Abreve Ccaron -30 KPX Abreve Ccedilla -30 KPX Abreve G -35 KPX Abreve Gbreve -35 KPX Abreve Gcommaaccent -35 KPX Abreve O -40 KPX Abreve Oacute -40 KPX Abreve Ocircumflex -40 KPX Abreve Odieresis -40 KPX Abreve Ograve -40 KPX Abreve Ohungarumlaut -40 KPX Abreve Omacron -40 KPX Abreve Oslash -40 KPX Abreve Otilde -40 KPX Abreve Q -40 KPX Abreve T -37 KPX Abreve Tcaron -37 KPX Abreve Tcommaaccent -37 KPX Abreve U -50 KPX Abreve Uacute -50 KPX Abreve Ucircumflex -50 KPX Abreve Udieresis -50 KPX Abreve Ugrave -50 KPX Abreve Uhungarumlaut -50 KPX Abreve Umacron -50 KPX Abreve Uogonek -50 KPX Abreve Uring -50 KPX Abreve V -105 KPX Abreve W -95 KPX Abreve Y -55 KPX Abreve Yacute -55 KPX Abreve Ydieresis -55 KPX Abreve quoteright -37 KPX Abreve u -20 KPX Abreve uacute -20 KPX Abreve ucircumflex -20 KPX Abreve udieresis -20 KPX Abreve ugrave -20 KPX Abreve uhungarumlaut -20 KPX Abreve umacron -20 KPX Abreve uogonek -20 KPX Abreve uring -20 KPX Abreve v -55 KPX Abreve w -55 KPX Abreve y -55 KPX Abreve yacute -55 KPX Abreve ydieresis -55 KPX Acircumflex C -30 KPX Acircumflex Cacute -30 KPX Acircumflex Ccaron -30 KPX Acircumflex Ccedilla -30 KPX Acircumflex G -35 KPX Acircumflex Gbreve -35 KPX Acircumflex Gcommaaccent -35 KPX Acircumflex O -40 KPX Acircumflex Oacute -40 KPX Acircumflex Ocircumflex -40 KPX Acircumflex Odieresis -40 KPX Acircumflex Ograve -40 KPX Acircumflex Ohungarumlaut -40 KPX Acircumflex Omacron -40 KPX Acircumflex Oslash -40 KPX Acircumflex Otilde -40 KPX Acircumflex Q -40 KPX Acircumflex T -37 KPX Acircumflex Tcaron -37 KPX Acircumflex Tcommaaccent -37 KPX Acircumflex U -50 KPX Acircumflex Uacute -50 KPX Acircumflex Ucircumflex -50 KPX Acircumflex Udieresis -50 KPX Acircumflex Ugrave -50 KPX Acircumflex Uhungarumlaut -50 KPX Acircumflex Umacron -50 KPX Acircumflex Uogonek -50 KPX Acircumflex Uring -50 KPX Acircumflex V -105 KPX Acircumflex W -95 KPX Acircumflex Y -55 KPX Acircumflex Yacute -55 KPX Acircumflex Ydieresis -55 KPX Acircumflex quoteright -37 KPX Acircumflex u -20 KPX Acircumflex uacute -20 KPX Acircumflex ucircumflex -20 KPX Acircumflex udieresis -20 KPX Acircumflex ugrave -20 KPX Acircumflex uhungarumlaut -20 KPX Acircumflex umacron -20 KPX Acircumflex uogonek -20 KPX Acircumflex uring -20 KPX Acircumflex v -55 KPX Acircumflex w -55 KPX Acircumflex y -55 KPX Acircumflex yacute -55 KPX Acircumflex ydieresis -55 KPX Adieresis C -30 KPX Adieresis Cacute -30 KPX Adieresis Ccaron -30 KPX Adieresis Ccedilla -30 KPX Adieresis G -35 KPX Adieresis Gbreve -35 KPX Adieresis Gcommaaccent -35 KPX Adieresis O -40 KPX Adieresis Oacute -40 KPX Adieresis Ocircumflex -40 KPX Adieresis Odieresis -40 KPX Adieresis Ograve -40 KPX Adieresis Ohungarumlaut -40 KPX Adieresis Omacron -40 KPX Adieresis Oslash -40 KPX Adieresis Otilde -40 KPX Adieresis Q -40 KPX Adieresis T -37 KPX Adieresis Tcaron -37 KPX Adieresis Tcommaaccent -37 KPX Adieresis U -50 KPX Adieresis Uacute -50 KPX Adieresis Ucircumflex -50 KPX Adieresis Udieresis -50 KPX Adieresis Ugrave -50 KPX Adieresis Uhungarumlaut -50 KPX Adieresis Umacron -50 KPX Adieresis Uogonek -50 KPX Adieresis Uring -50 KPX Adieresis V -105 KPX Adieresis W -95 KPX Adieresis Y -55 KPX Adieresis Yacute -55 KPX Adieresis Ydieresis -55 KPX Adieresis quoteright -37 KPX Adieresis u -20 KPX Adieresis uacute -20 KPX Adieresis ucircumflex -20 KPX Adieresis udieresis -20 KPX Adieresis ugrave -20 KPX Adieresis uhungarumlaut -20 KPX Adieresis umacron -20 KPX Adieresis uogonek -20 KPX Adieresis uring -20 KPX Adieresis v -55 KPX Adieresis w -55 KPX Adieresis y -55 KPX Adieresis yacute -55 KPX Adieresis ydieresis -55 KPX Agrave C -30 KPX Agrave Cacute -30 KPX Agrave Ccaron -30 KPX Agrave Ccedilla -30 KPX Agrave G -35 KPX Agrave Gbreve -35 KPX Agrave Gcommaaccent -35 KPX Agrave O -40 KPX Agrave Oacute -40 KPX Agrave Ocircumflex -40 KPX Agrave Odieresis -40 KPX Agrave Ograve -40 KPX Agrave Ohungarumlaut -40 KPX Agrave Omacron -40 KPX Agrave Oslash -40 KPX Agrave Otilde -40 KPX Agrave Q -40 KPX Agrave T -37 KPX Agrave Tcaron -37 KPX Agrave Tcommaaccent -37 KPX Agrave U -50 KPX Agrave Uacute -50 KPX Agrave Ucircumflex -50 KPX Agrave Udieresis -50 KPX Agrave Ugrave -50 KPX Agrave Uhungarumlaut -50 KPX Agrave Umacron -50 KPX Agrave Uogonek -50 KPX Agrave Uring -50 KPX Agrave V -105 KPX Agrave W -95 KPX Agrave Y -55 KPX Agrave Yacute -55 KPX Agrave Ydieresis -55 KPX Agrave quoteright -37 KPX Agrave u -20 KPX Agrave uacute -20 KPX Agrave ucircumflex -20 KPX Agrave udieresis -20 KPX Agrave ugrave -20 KPX Agrave uhungarumlaut -20 KPX Agrave umacron -20 KPX Agrave uogonek -20 KPX Agrave uring -20 KPX Agrave v -55 KPX Agrave w -55 KPX Agrave y -55 KPX Agrave yacute -55 KPX Agrave ydieresis -55 KPX Amacron C -30 KPX Amacron Cacute -30 KPX Amacron Ccaron -30 KPX Amacron Ccedilla -30 KPX Amacron G -35 KPX Amacron Gbreve -35 KPX Amacron Gcommaaccent -35 KPX Amacron O -40 KPX Amacron Oacute -40 KPX Amacron Ocircumflex -40 KPX Amacron Odieresis -40 KPX Amacron Ograve -40 KPX Amacron Ohungarumlaut -40 KPX Amacron Omacron -40 KPX Amacron Oslash -40 KPX Amacron Otilde -40 KPX Amacron Q -40 KPX Amacron T -37 KPX Amacron Tcaron -37 KPX Amacron Tcommaaccent -37 KPX Amacron U -50 KPX Amacron Uacute -50 KPX Amacron Ucircumflex -50 KPX Amacron Udieresis -50 KPX Amacron Ugrave -50 KPX Amacron Uhungarumlaut -50 KPX Amacron Umacron -50 KPX Amacron Uogonek -50 KPX Amacron Uring -50 KPX Amacron V -105 KPX Amacron W -95 KPX Amacron Y -55 KPX Amacron Yacute -55 KPX Amacron Ydieresis -55 KPX Amacron quoteright -37 KPX Amacron u -20 KPX Amacron uacute -20 KPX Amacron ucircumflex -20 KPX Amacron udieresis -20 KPX Amacron ugrave -20 KPX Amacron uhungarumlaut -20 KPX Amacron umacron -20 KPX Amacron uogonek -20 KPX Amacron uring -20 KPX Amacron v -55 KPX Amacron w -55 KPX Amacron y -55 KPX Amacron yacute -55 KPX Amacron ydieresis -55 KPX Aogonek C -30 KPX Aogonek Cacute -30 KPX Aogonek Ccaron -30 KPX Aogonek Ccedilla -30 KPX Aogonek G -35 KPX Aogonek Gbreve -35 KPX Aogonek Gcommaaccent -35 KPX Aogonek O -40 KPX Aogonek Oacute -40 KPX Aogonek Ocircumflex -40 KPX Aogonek Odieresis -40 KPX Aogonek Ograve -40 KPX Aogonek Ohungarumlaut -40 KPX Aogonek Omacron -40 KPX Aogonek Oslash -40 KPX Aogonek Otilde -40 KPX Aogonek Q -40 KPX Aogonek T -37 KPX Aogonek Tcaron -37 KPX Aogonek Tcommaaccent -37 KPX Aogonek U -50 KPX Aogonek Uacute -50 KPX Aogonek Ucircumflex -50 KPX Aogonek Udieresis -50 KPX Aogonek Ugrave -50 KPX Aogonek Uhungarumlaut -50 KPX Aogonek Umacron -50 KPX Aogonek Uogonek -50 KPX Aogonek Uring -50 KPX Aogonek V -105 KPX Aogonek W -95 KPX Aogonek Y -55 KPX Aogonek Yacute -55 KPX Aogonek Ydieresis -55 KPX Aogonek quoteright -37 KPX Aogonek u -20 KPX Aogonek uacute -20 KPX Aogonek ucircumflex -20 KPX Aogonek udieresis -20 KPX Aogonek ugrave -20 KPX Aogonek uhungarumlaut -20 KPX Aogonek umacron -20 KPX Aogonek uogonek -20 KPX Aogonek uring -20 KPX Aogonek v -55 KPX Aogonek w -55 KPX Aogonek y -55 KPX Aogonek yacute -55 KPX Aogonek ydieresis -55 KPX Aring C -30 KPX Aring Cacute -30 KPX Aring Ccaron -30 KPX Aring Ccedilla -30 KPX Aring G -35 KPX Aring Gbreve -35 KPX Aring Gcommaaccent -35 KPX Aring O -40 KPX Aring Oacute -40 KPX Aring Ocircumflex -40 KPX Aring Odieresis -40 KPX Aring Ograve -40 KPX Aring Ohungarumlaut -40 KPX Aring Omacron -40 KPX Aring Oslash -40 KPX Aring Otilde -40 KPX Aring Q -40 KPX Aring T -37 KPX Aring Tcaron -37 KPX Aring Tcommaaccent -37 KPX Aring U -50 KPX Aring Uacute -50 KPX Aring Ucircumflex -50 KPX Aring Udieresis -50 KPX Aring Ugrave -50 KPX Aring Uhungarumlaut -50 KPX Aring Umacron -50 KPX Aring Uogonek -50 KPX Aring Uring -50 KPX Aring V -105 KPX Aring W -95 KPX Aring Y -55 KPX Aring Yacute -55 KPX Aring Ydieresis -55 KPX Aring quoteright -37 KPX Aring u -20 KPX Aring uacute -20 KPX Aring ucircumflex -20 KPX Aring udieresis -20 KPX Aring ugrave -20 KPX Aring uhungarumlaut -20 KPX Aring umacron -20 KPX Aring uogonek -20 KPX Aring uring -20 KPX Aring v -55 KPX Aring w -55 KPX Aring y -55 KPX Aring yacute -55 KPX Aring ydieresis -55 KPX Atilde C -30 KPX Atilde Cacute -30 KPX Atilde Ccaron -30 KPX Atilde Ccedilla -30 KPX Atilde G -35 KPX Atilde Gbreve -35 KPX Atilde Gcommaaccent -35 KPX Atilde O -40 KPX Atilde Oacute -40 KPX Atilde Ocircumflex -40 KPX Atilde Odieresis -40 KPX Atilde Ograve -40 KPX Atilde Ohungarumlaut -40 KPX Atilde Omacron -40 KPX Atilde Oslash -40 KPX Atilde Otilde -40 KPX Atilde Q -40 KPX Atilde T -37 KPX Atilde Tcaron -37 KPX Atilde Tcommaaccent -37 KPX Atilde U -50 KPX Atilde Uacute -50 KPX Atilde Ucircumflex -50 KPX Atilde Udieresis -50 KPX Atilde Ugrave -50 KPX Atilde Uhungarumlaut -50 KPX Atilde Umacron -50 KPX Atilde Uogonek -50 KPX Atilde Uring -50 KPX Atilde V -105 KPX Atilde W -95 KPX Atilde Y -55 KPX Atilde Yacute -55 KPX Atilde Ydieresis -55 KPX Atilde quoteright -37 KPX Atilde u -20 KPX Atilde uacute -20 KPX Atilde ucircumflex -20 KPX Atilde udieresis -20 KPX Atilde ugrave -20 KPX Atilde uhungarumlaut -20 KPX Atilde umacron -20 KPX Atilde uogonek -20 KPX Atilde uring -20 KPX Atilde v -55 KPX Atilde w -55 KPX Atilde y -55 KPX Atilde yacute -55 KPX Atilde ydieresis -55 KPX B A -25 KPX B Aacute -25 KPX B Abreve -25 KPX B Acircumflex -25 KPX B Adieresis -25 KPX B Agrave -25 KPX B Amacron -25 KPX B Aogonek -25 KPX B Aring -25 KPX B Atilde -25 KPX B U -10 KPX B Uacute -10 KPX B Ucircumflex -10 KPX B Udieresis -10 KPX B Ugrave -10 KPX B Uhungarumlaut -10 KPX B Umacron -10 KPX B Uogonek -10 KPX B Uring -10 KPX D A -35 KPX D Aacute -35 KPX D Abreve -35 KPX D Acircumflex -35 KPX D Adieresis -35 KPX D Agrave -35 KPX D Amacron -35 KPX D Aogonek -35 KPX D Aring -35 KPX D Atilde -35 KPX D V -40 KPX D W -40 KPX D Y -40 KPX D Yacute -40 KPX D Ydieresis -40 KPX Dcaron A -35 KPX Dcaron Aacute -35 KPX Dcaron Abreve -35 KPX Dcaron Acircumflex -35 KPX Dcaron Adieresis -35 KPX Dcaron Agrave -35 KPX Dcaron Amacron -35 KPX Dcaron Aogonek -35 KPX Dcaron Aring -35 KPX Dcaron Atilde -35 KPX Dcaron V -40 KPX Dcaron W -40 KPX Dcaron Y -40 KPX Dcaron Yacute -40 KPX Dcaron Ydieresis -40 KPX Dcroat A -35 KPX Dcroat Aacute -35 KPX Dcroat Abreve -35 KPX Dcroat Acircumflex -35 KPX Dcroat Adieresis -35 KPX Dcroat Agrave -35 KPX Dcroat Amacron -35 KPX Dcroat Aogonek -35 KPX Dcroat Aring -35 KPX Dcroat Atilde -35 KPX Dcroat V -40 KPX Dcroat W -40 KPX Dcroat Y -40 KPX Dcroat Yacute -40 KPX Dcroat Ydieresis -40 KPX F A -115 KPX F Aacute -115 KPX F Abreve -115 KPX F Acircumflex -115 KPX F Adieresis -115 KPX F Agrave -115 KPX F Amacron -115 KPX F Aogonek -115 KPX F Aring -115 KPX F Atilde -115 KPX F a -75 KPX F aacute -75 KPX F abreve -75 KPX F acircumflex -75 KPX F adieresis -75 KPX F agrave -75 KPX F amacron -75 KPX F aogonek -75 KPX F aring -75 KPX F atilde -75 KPX F comma -135 KPX F e -75 KPX F eacute -75 KPX F ecaron -75 KPX F ecircumflex -75 KPX F edieresis -75 KPX F edotaccent -75 KPX F egrave -75 KPX F emacron -75 KPX F eogonek -75 KPX F i -45 KPX F iacute -45 KPX F icircumflex -45 KPX F idieresis -45 KPX F igrave -45 KPX F imacron -45 KPX F iogonek -45 KPX F o -105 KPX F oacute -105 KPX F ocircumflex -105 KPX F odieresis -105 KPX F ograve -105 KPX F ohungarumlaut -105 KPX F omacron -105 KPX F oslash -105 KPX F otilde -105 KPX F period -135 KPX F r -55 KPX F racute -55 KPX F rcaron -55 KPX F rcommaaccent -55 KPX J A -40 KPX J Aacute -40 KPX J Abreve -40 KPX J Acircumflex -40 KPX J Adieresis -40 KPX J Agrave -40 KPX J Amacron -40 KPX J Aogonek -40 KPX J Aring -40 KPX J Atilde -40 KPX J a -35 KPX J aacute -35 KPX J abreve -35 KPX J acircumflex -35 KPX J adieresis -35 KPX J agrave -35 KPX J amacron -35 KPX J aogonek -35 KPX J aring -35 KPX J atilde -35 KPX J comma -25 KPX J e -25 KPX J eacute -25 KPX J ecaron -25 KPX J ecircumflex -25 KPX J edieresis -25 KPX J edotaccent -25 KPX J egrave -25 KPX J emacron -25 KPX J eogonek -25 KPX J o -25 KPX J oacute -25 KPX J ocircumflex -25 KPX J odieresis -25 KPX J ograve -25 KPX J ohungarumlaut -25 KPX J omacron -25 KPX J oslash -25 KPX J otilde -25 KPX J period -25 KPX J u -35 KPX J uacute -35 KPX J ucircumflex -35 KPX J udieresis -35 KPX J ugrave -35 KPX J uhungarumlaut -35 KPX J umacron -35 KPX J uogonek -35 KPX J uring -35 KPX K O -50 KPX K Oacute -50 KPX K Ocircumflex -50 KPX K Odieresis -50 KPX K Ograve -50 KPX K Ohungarumlaut -50 KPX K Omacron -50 KPX K Oslash -50 KPX K Otilde -50 KPX K e -35 KPX K eacute -35 KPX K ecaron -35 KPX K ecircumflex -35 KPX K edieresis -35 KPX K edotaccent -35 KPX K egrave -35 KPX K emacron -35 KPX K eogonek -35 KPX K o -40 KPX K oacute -40 KPX K ocircumflex -40 KPX K odieresis -40 KPX K ograve -40 KPX K ohungarumlaut -40 KPX K omacron -40 KPX K oslash -40 KPX K otilde -40 KPX K u -40 KPX K uacute -40 KPX K ucircumflex -40 KPX K udieresis -40 KPX K ugrave -40 KPX K uhungarumlaut -40 KPX K umacron -40 KPX K uogonek -40 KPX K uring -40 KPX K y -40 KPX K yacute -40 KPX K ydieresis -40 KPX Kcommaaccent O -50 KPX Kcommaaccent Oacute -50 KPX Kcommaaccent Ocircumflex -50 KPX Kcommaaccent Odieresis -50 KPX Kcommaaccent Ograve -50 KPX Kcommaaccent Ohungarumlaut -50 KPX Kcommaaccent Omacron -50 KPX Kcommaaccent Oslash -50 KPX Kcommaaccent Otilde -50 KPX Kcommaaccent e -35 KPX Kcommaaccent eacute -35 KPX Kcommaaccent ecaron -35 KPX Kcommaaccent ecircumflex -35 KPX Kcommaaccent edieresis -35 KPX Kcommaaccent edotaccent -35 KPX Kcommaaccent egrave -35 KPX Kcommaaccent emacron -35 KPX Kcommaaccent eogonek -35 KPX Kcommaaccent o -40 KPX Kcommaaccent oacute -40 KPX Kcommaaccent ocircumflex -40 KPX Kcommaaccent odieresis -40 KPX Kcommaaccent ograve -40 KPX Kcommaaccent ohungarumlaut -40 KPX Kcommaaccent omacron -40 KPX Kcommaaccent oslash -40 KPX Kcommaaccent otilde -40 KPX Kcommaaccent u -40 KPX Kcommaaccent uacute -40 KPX Kcommaaccent ucircumflex -40 KPX Kcommaaccent udieresis -40 KPX Kcommaaccent ugrave -40 KPX Kcommaaccent uhungarumlaut -40 KPX Kcommaaccent umacron -40 KPX Kcommaaccent uogonek -40 KPX Kcommaaccent uring -40 KPX Kcommaaccent y -40 KPX Kcommaaccent yacute -40 KPX Kcommaaccent ydieresis -40 KPX L T -20 KPX L Tcaron -20 KPX L Tcommaaccent -20 KPX L V -55 KPX L W -55 KPX L Y -20 KPX L Yacute -20 KPX L Ydieresis -20 KPX L quoteright -37 KPX L y -30 KPX L yacute -30 KPX L ydieresis -30 KPX Lacute T -20 KPX Lacute Tcaron -20 KPX Lacute Tcommaaccent -20 KPX Lacute V -55 KPX Lacute W -55 KPX Lacute Y -20 KPX Lacute Yacute -20 KPX Lacute Ydieresis -20 KPX Lacute quoteright -37 KPX Lacute y -30 KPX Lacute yacute -30 KPX Lacute ydieresis -30 KPX Lcommaaccent T -20 KPX Lcommaaccent Tcaron -20 KPX Lcommaaccent Tcommaaccent -20 KPX Lcommaaccent V -55 KPX Lcommaaccent W -55 KPX Lcommaaccent Y -20 KPX Lcommaaccent Yacute -20 KPX Lcommaaccent Ydieresis -20 KPX Lcommaaccent quoteright -37 KPX Lcommaaccent y -30 KPX Lcommaaccent yacute -30 KPX Lcommaaccent ydieresis -30 KPX Lslash T -20 KPX Lslash Tcaron -20 KPX Lslash Tcommaaccent -20 KPX Lslash V -55 KPX Lslash W -55 KPX Lslash Y -20 KPX Lslash Yacute -20 KPX Lslash Ydieresis -20 KPX Lslash quoteright -37 KPX Lslash y -30 KPX Lslash yacute -30 KPX Lslash ydieresis -30 KPX N A -27 KPX N Aacute -27 KPX N Abreve -27 KPX N Acircumflex -27 KPX N Adieresis -27 KPX N Agrave -27 KPX N Amacron -27 KPX N Aogonek -27 KPX N Aring -27 KPX N Atilde -27 KPX Nacute A -27 KPX Nacute Aacute -27 KPX Nacute Abreve -27 KPX Nacute Acircumflex -27 KPX Nacute Adieresis -27 KPX Nacute Agrave -27 KPX Nacute Amacron -27 KPX Nacute Aogonek -27 KPX Nacute Aring -27 KPX Nacute Atilde -27 KPX Ncaron A -27 KPX Ncaron Aacute -27 KPX Ncaron Abreve -27 KPX Ncaron Acircumflex -27 KPX Ncaron Adieresis -27 KPX Ncaron Agrave -27 KPX Ncaron Amacron -27 KPX Ncaron Aogonek -27 KPX Ncaron Aring -27 KPX Ncaron Atilde -27 KPX Ncommaaccent A -27 KPX Ncommaaccent Aacute -27 KPX Ncommaaccent Abreve -27 KPX Ncommaaccent Acircumflex -27 KPX Ncommaaccent Adieresis -27 KPX Ncommaaccent Agrave -27 KPX Ncommaaccent Amacron -27 KPX Ncommaaccent Aogonek -27 KPX Ncommaaccent Aring -27 KPX Ncommaaccent Atilde -27 KPX Ntilde A -27 KPX Ntilde Aacute -27 KPX Ntilde Abreve -27 KPX Ntilde Acircumflex -27 KPX Ntilde Adieresis -27 KPX Ntilde Agrave -27 KPX Ntilde Amacron -27 KPX Ntilde Aogonek -27 KPX Ntilde Aring -27 KPX Ntilde Atilde -27 KPX O A -55 KPX O Aacute -55 KPX O Abreve -55 KPX O Acircumflex -55 KPX O Adieresis -55 KPX O Agrave -55 KPX O Amacron -55 KPX O Aogonek -55 KPX O Aring -55 KPX O Atilde -55 KPX O T -40 KPX O Tcaron -40 KPX O Tcommaaccent -40 KPX O V -50 KPX O W -50 KPX O X -40 KPX O Y -50 KPX O Yacute -50 KPX O Ydieresis -50 KPX Oacute A -55 KPX Oacute Aacute -55 KPX Oacute Abreve -55 KPX Oacute Acircumflex -55 KPX Oacute Adieresis -55 KPX Oacute Agrave -55 KPX Oacute Amacron -55 KPX Oacute Aogonek -55 KPX Oacute Aring -55 KPX Oacute Atilde -55 KPX Oacute T -40 KPX Oacute Tcaron -40 KPX Oacute Tcommaaccent -40 KPX Oacute V -50 KPX Oacute W -50 KPX Oacute X -40 KPX Oacute Y -50 KPX Oacute Yacute -50 KPX Oacute Ydieresis -50 KPX Ocircumflex A -55 KPX Ocircumflex Aacute -55 KPX Ocircumflex Abreve -55 KPX Ocircumflex Acircumflex -55 KPX Ocircumflex Adieresis -55 KPX Ocircumflex Agrave -55 KPX Ocircumflex Amacron -55 KPX Ocircumflex Aogonek -55 KPX Ocircumflex Aring -55 KPX Ocircumflex Atilde -55 KPX Ocircumflex T -40 KPX Ocircumflex Tcaron -40 KPX Ocircumflex Tcommaaccent -40 KPX Ocircumflex V -50 KPX Ocircumflex W -50 KPX Ocircumflex X -40 KPX Ocircumflex Y -50 KPX Ocircumflex Yacute -50 KPX Ocircumflex Ydieresis -50 KPX Odieresis A -55 KPX Odieresis Aacute -55 KPX Odieresis Abreve -55 KPX Odieresis Acircumflex -55 KPX Odieresis Adieresis -55 KPX Odieresis Agrave -55 KPX Odieresis Amacron -55 KPX Odieresis Aogonek -55 KPX Odieresis Aring -55 KPX Odieresis Atilde -55 KPX Odieresis T -40 KPX Odieresis Tcaron -40 KPX Odieresis Tcommaaccent -40 KPX Odieresis V -50 KPX Odieresis W -50 KPX Odieresis X -40 KPX Odieresis Y -50 KPX Odieresis Yacute -50 KPX Odieresis Ydieresis -50 KPX Ograve A -55 KPX Ograve Aacute -55 KPX Ograve Abreve -55 KPX Ograve Acircumflex -55 KPX Ograve Adieresis -55 KPX Ograve Agrave -55 KPX Ograve Amacron -55 KPX Ograve Aogonek -55 KPX Ograve Aring -55 KPX Ograve Atilde -55 KPX Ograve T -40 KPX Ograve Tcaron -40 KPX Ograve Tcommaaccent -40 KPX Ograve V -50 KPX Ograve W -50 KPX Ograve X -40 KPX Ograve Y -50 KPX Ograve Yacute -50 KPX Ograve Ydieresis -50 KPX Ohungarumlaut A -55 KPX Ohungarumlaut Aacute -55 KPX Ohungarumlaut Abreve -55 KPX Ohungarumlaut Acircumflex -55 KPX Ohungarumlaut Adieresis -55 KPX Ohungarumlaut Agrave -55 KPX Ohungarumlaut Amacron -55 KPX Ohungarumlaut Aogonek -55 KPX Ohungarumlaut Aring -55 KPX Ohungarumlaut Atilde -55 KPX Ohungarumlaut T -40 KPX Ohungarumlaut Tcaron -40 KPX Ohungarumlaut Tcommaaccent -40 KPX Ohungarumlaut V -50 KPX Ohungarumlaut W -50 KPX Ohungarumlaut X -40 KPX Ohungarumlaut Y -50 KPX Ohungarumlaut Yacute -50 KPX Ohungarumlaut Ydieresis -50 KPX Omacron A -55 KPX Omacron Aacute -55 KPX Omacron Abreve -55 KPX Omacron Acircumflex -55 KPX Omacron Adieresis -55 KPX Omacron Agrave -55 KPX Omacron Amacron -55 KPX Omacron Aogonek -55 KPX Omacron Aring -55 KPX Omacron Atilde -55 KPX Omacron T -40 KPX Omacron Tcaron -40 KPX Omacron Tcommaaccent -40 KPX Omacron V -50 KPX Omacron W -50 KPX Omacron X -40 KPX Omacron Y -50 KPX Omacron Yacute -50 KPX Omacron Ydieresis -50 KPX Oslash A -55 KPX Oslash Aacute -55 KPX Oslash Abreve -55 KPX Oslash Acircumflex -55 KPX Oslash Adieresis -55 KPX Oslash Agrave -55 KPX Oslash Amacron -55 KPX Oslash Aogonek -55 KPX Oslash Aring -55 KPX Oslash Atilde -55 KPX Oslash T -40 KPX Oslash Tcaron -40 KPX Oslash Tcommaaccent -40 KPX Oslash V -50 KPX Oslash W -50 KPX Oslash X -40 KPX Oslash Y -50 KPX Oslash Yacute -50 KPX Oslash Ydieresis -50 KPX Otilde A -55 KPX Otilde Aacute -55 KPX Otilde Abreve -55 KPX Otilde Acircumflex -55 KPX Otilde Adieresis -55 KPX Otilde Agrave -55 KPX Otilde Amacron -55 KPX Otilde Aogonek -55 KPX Otilde Aring -55 KPX Otilde Atilde -55 KPX Otilde T -40 KPX Otilde Tcaron -40 KPX Otilde Tcommaaccent -40 KPX Otilde V -50 KPX Otilde W -50 KPX Otilde X -40 KPX Otilde Y -50 KPX Otilde Yacute -50 KPX Otilde Ydieresis -50 KPX P A -90 KPX P Aacute -90 KPX P Abreve -90 KPX P Acircumflex -90 KPX P Adieresis -90 KPX P Agrave -90 KPX P Amacron -90 KPX P Aogonek -90 KPX P Aring -90 KPX P Atilde -90 KPX P a -80 KPX P aacute -80 KPX P abreve -80 KPX P acircumflex -80 KPX P adieresis -80 KPX P agrave -80 KPX P amacron -80 KPX P aogonek -80 KPX P aring -80 KPX P atilde -80 KPX P comma -135 KPX P e -80 KPX P eacute -80 KPX P ecaron -80 KPX P ecircumflex -80 KPX P edieresis -80 KPX P edotaccent -80 KPX P egrave -80 KPX P emacron -80 KPX P eogonek -80 KPX P o -80 KPX P oacute -80 KPX P ocircumflex -80 KPX P odieresis -80 KPX P ograve -80 KPX P ohungarumlaut -80 KPX P omacron -80 KPX P oslash -80 KPX P otilde -80 KPX P period -135 KPX Q U -10 KPX Q Uacute -10 KPX Q Ucircumflex -10 KPX Q Udieresis -10 KPX Q Ugrave -10 KPX Q Uhungarumlaut -10 KPX Q Umacron -10 KPX Q Uogonek -10 KPX Q Uring -10 KPX R O -40 KPX R Oacute -40 KPX R Ocircumflex -40 KPX R Odieresis -40 KPX R Ograve -40 KPX R Ohungarumlaut -40 KPX R Omacron -40 KPX R Oslash -40 KPX R Otilde -40 KPX R U -40 KPX R Uacute -40 KPX R Ucircumflex -40 KPX R Udieresis -40 KPX R Ugrave -40 KPX R Uhungarumlaut -40 KPX R Umacron -40 KPX R Uogonek -40 KPX R Uring -40 KPX R V -18 KPX R W -18 KPX R Y -18 KPX R Yacute -18 KPX R Ydieresis -18 KPX Racute O -40 KPX Racute Oacute -40 KPX Racute Ocircumflex -40 KPX Racute Odieresis -40 KPX Racute Ograve -40 KPX Racute Ohungarumlaut -40 KPX Racute Omacron -40 KPX Racute Oslash -40 KPX Racute Otilde -40 KPX Racute U -40 KPX Racute Uacute -40 KPX Racute Ucircumflex -40 KPX Racute Udieresis -40 KPX Racute Ugrave -40 KPX Racute Uhungarumlaut -40 KPX Racute Umacron -40 KPX Racute Uogonek -40 KPX Racute Uring -40 KPX Racute V -18 KPX Racute W -18 KPX Racute Y -18 KPX Racute Yacute -18 KPX Racute Ydieresis -18 KPX Rcaron O -40 KPX Rcaron Oacute -40 KPX Rcaron Ocircumflex -40 KPX Rcaron Odieresis -40 KPX Rcaron Ograve -40 KPX Rcaron Ohungarumlaut -40 KPX Rcaron Omacron -40 KPX Rcaron Oslash -40 KPX Rcaron Otilde -40 KPX Rcaron U -40 KPX Rcaron Uacute -40 KPX Rcaron Ucircumflex -40 KPX Rcaron Udieresis -40 KPX Rcaron Ugrave -40 KPX Rcaron Uhungarumlaut -40 KPX Rcaron Umacron -40 KPX Rcaron Uogonek -40 KPX Rcaron Uring -40 KPX Rcaron V -18 KPX Rcaron W -18 KPX Rcaron Y -18 KPX Rcaron Yacute -18 KPX Rcaron Ydieresis -18 KPX Rcommaaccent O -40 KPX Rcommaaccent Oacute -40 KPX Rcommaaccent Ocircumflex -40 KPX Rcommaaccent Odieresis -40 KPX Rcommaaccent Ograve -40 KPX Rcommaaccent Ohungarumlaut -40 KPX Rcommaaccent Omacron -40 KPX Rcommaaccent Oslash -40 KPX Rcommaaccent Otilde -40 KPX Rcommaaccent U -40 KPX Rcommaaccent Uacute -40 KPX Rcommaaccent Ucircumflex -40 KPX Rcommaaccent Udieresis -40 KPX Rcommaaccent Ugrave -40 KPX Rcommaaccent Uhungarumlaut -40 KPX Rcommaaccent Umacron -40 KPX Rcommaaccent Uogonek -40 KPX Rcommaaccent Uring -40 KPX Rcommaaccent V -18 KPX Rcommaaccent W -18 KPX Rcommaaccent Y -18 KPX Rcommaaccent Yacute -18 KPX Rcommaaccent Ydieresis -18 KPX T A -50 KPX T Aacute -50 KPX T Abreve -50 KPX T Acircumflex -50 KPX T Adieresis -50 KPX T Agrave -50 KPX T Amacron -50 KPX T Aogonek -50 KPX T Aring -50 KPX T Atilde -50 KPX T O -18 KPX T Oacute -18 KPX T Ocircumflex -18 KPX T Odieresis -18 KPX T Ograve -18 KPX T Ohungarumlaut -18 KPX T Omacron -18 KPX T Oslash -18 KPX T Otilde -18 KPX T a -92 KPX T aacute -92 KPX T abreve -92 KPX T acircumflex -92 KPX T adieresis -92 KPX T agrave -92 KPX T amacron -92 KPX T aogonek -92 KPX T aring -92 KPX T atilde -92 KPX T colon -55 KPX T comma -74 KPX T e -92 KPX T eacute -92 KPX T ecaron -92 KPX T ecircumflex -52 KPX T edieresis -52 KPX T edotaccent -92 KPX T egrave -52 KPX T emacron -52 KPX T eogonek -92 KPX T hyphen -74 KPX T i -55 KPX T iacute -55 KPX T iogonek -55 KPX T o -92 KPX T oacute -92 KPX T ocircumflex -92 KPX T odieresis -92 KPX T ograve -92 KPX T ohungarumlaut -92 KPX T omacron -92 KPX T oslash -92 KPX T otilde -92 KPX T period -74 KPX T r -55 KPX T racute -55 KPX T rcaron -55 KPX T rcommaaccent -55 KPX T semicolon -65 KPX T u -55 KPX T uacute -55 KPX T ucircumflex -55 KPX T udieresis -55 KPX T ugrave -55 KPX T uhungarumlaut -55 KPX T umacron -55 KPX T uogonek -55 KPX T uring -55 KPX T w -74 KPX T y -74 KPX T yacute -74 KPX T ydieresis -34 KPX Tcaron A -50 KPX Tcaron Aacute -50 KPX Tcaron Abreve -50 KPX Tcaron Acircumflex -50 KPX Tcaron Adieresis -50 KPX Tcaron Agrave -50 KPX Tcaron Amacron -50 KPX Tcaron Aogonek -50 KPX Tcaron Aring -50 KPX Tcaron Atilde -50 KPX Tcaron O -18 KPX Tcaron Oacute -18 KPX Tcaron Ocircumflex -18 KPX Tcaron Odieresis -18 KPX Tcaron Ograve -18 KPX Tcaron Ohungarumlaut -18 KPX Tcaron Omacron -18 KPX Tcaron Oslash -18 KPX Tcaron Otilde -18 KPX Tcaron a -92 KPX Tcaron aacute -92 KPX Tcaron abreve -92 KPX Tcaron acircumflex -92 KPX Tcaron adieresis -92 KPX Tcaron agrave -92 KPX Tcaron amacron -92 KPX Tcaron aogonek -92 KPX Tcaron aring -92 KPX Tcaron atilde -92 KPX Tcaron colon -55 KPX Tcaron comma -74 KPX Tcaron e -92 KPX Tcaron eacute -92 KPX Tcaron ecaron -92 KPX Tcaron ecircumflex -52 KPX Tcaron edieresis -52 KPX Tcaron edotaccent -92 KPX Tcaron egrave -52 KPX Tcaron emacron -52 KPX Tcaron eogonek -92 KPX Tcaron hyphen -74 KPX Tcaron i -55 KPX Tcaron iacute -55 KPX Tcaron iogonek -55 KPX Tcaron o -92 KPX Tcaron oacute -92 KPX Tcaron ocircumflex -92 KPX Tcaron odieresis -92 KPX Tcaron ograve -92 KPX Tcaron ohungarumlaut -92 KPX Tcaron omacron -92 KPX Tcaron oslash -92 KPX Tcaron otilde -92 KPX Tcaron period -74 KPX Tcaron r -55 KPX Tcaron racute -55 KPX Tcaron rcaron -55 KPX Tcaron rcommaaccent -55 KPX Tcaron semicolon -65 KPX Tcaron u -55 KPX Tcaron uacute -55 KPX Tcaron ucircumflex -55 KPX Tcaron udieresis -55 KPX Tcaron ugrave -55 KPX Tcaron uhungarumlaut -55 KPX Tcaron umacron -55 KPX Tcaron uogonek -55 KPX Tcaron uring -55 KPX Tcaron w -74 KPX Tcaron y -74 KPX Tcaron yacute -74 KPX Tcaron ydieresis -34 KPX Tcommaaccent A -50 KPX Tcommaaccent Aacute -50 KPX Tcommaaccent Abreve -50 KPX Tcommaaccent Acircumflex -50 KPX Tcommaaccent Adieresis -50 KPX Tcommaaccent Agrave -50 KPX Tcommaaccent Amacron -50 KPX Tcommaaccent Aogonek -50 KPX Tcommaaccent Aring -50 KPX Tcommaaccent Atilde -50 KPX Tcommaaccent O -18 KPX Tcommaaccent Oacute -18 KPX Tcommaaccent Ocircumflex -18 KPX Tcommaaccent Odieresis -18 KPX Tcommaaccent Ograve -18 KPX Tcommaaccent Ohungarumlaut -18 KPX Tcommaaccent Omacron -18 KPX Tcommaaccent Oslash -18 KPX Tcommaaccent Otilde -18 KPX Tcommaaccent a -92 KPX Tcommaaccent aacute -92 KPX Tcommaaccent abreve -92 KPX Tcommaaccent acircumflex -92 KPX Tcommaaccent adieresis -92 KPX Tcommaaccent agrave -92 KPX Tcommaaccent amacron -92 KPX Tcommaaccent aogonek -92 KPX Tcommaaccent aring -92 KPX Tcommaaccent atilde -92 KPX Tcommaaccent colon -55 KPX Tcommaaccent comma -74 KPX Tcommaaccent e -92 KPX Tcommaaccent eacute -92 KPX Tcommaaccent ecaron -92 KPX Tcommaaccent ecircumflex -52 KPX Tcommaaccent edieresis -52 KPX Tcommaaccent edotaccent -92 KPX Tcommaaccent egrave -52 KPX Tcommaaccent emacron -52 KPX Tcommaaccent eogonek -92 KPX Tcommaaccent hyphen -74 KPX Tcommaaccent i -55 KPX Tcommaaccent iacute -55 KPX Tcommaaccent iogonek -55 KPX Tcommaaccent o -92 KPX Tcommaaccent oacute -92 KPX Tcommaaccent ocircumflex -92 KPX Tcommaaccent odieresis -92 KPX Tcommaaccent ograve -92 KPX Tcommaaccent ohungarumlaut -92 KPX Tcommaaccent omacron -92 KPX Tcommaaccent oslash -92 KPX Tcommaaccent otilde -92 KPX Tcommaaccent period -74 KPX Tcommaaccent r -55 KPX Tcommaaccent racute -55 KPX Tcommaaccent rcaron -55 KPX Tcommaaccent rcommaaccent -55 KPX Tcommaaccent semicolon -65 KPX Tcommaaccent u -55 KPX Tcommaaccent uacute -55 KPX Tcommaaccent ucircumflex -55 KPX Tcommaaccent udieresis -55 KPX Tcommaaccent ugrave -55 KPX Tcommaaccent uhungarumlaut -55 KPX Tcommaaccent umacron -55 KPX Tcommaaccent uogonek -55 KPX Tcommaaccent uring -55 KPX Tcommaaccent w -74 KPX Tcommaaccent y -74 KPX Tcommaaccent yacute -74 KPX Tcommaaccent ydieresis -34 KPX U A -40 KPX U Aacute -40 KPX U Abreve -40 KPX U Acircumflex -40 KPX U Adieresis -40 KPX U Agrave -40 KPX U Amacron -40 KPX U Aogonek -40 KPX U Aring -40 KPX U Atilde -40 KPX U comma -25 KPX U period -25 KPX Uacute A -40 KPX Uacute Aacute -40 KPX Uacute Abreve -40 KPX Uacute Acircumflex -40 KPX Uacute Adieresis -40 KPX Uacute Agrave -40 KPX Uacute Amacron -40 KPX Uacute Aogonek -40 KPX Uacute Aring -40 KPX Uacute Atilde -40 KPX Uacute comma -25 KPX Uacute period -25 KPX Ucircumflex A -40 KPX Ucircumflex Aacute -40 KPX Ucircumflex Abreve -40 KPX Ucircumflex Acircumflex -40 KPX Ucircumflex Adieresis -40 KPX Ucircumflex Agrave -40 KPX Ucircumflex Amacron -40 KPX Ucircumflex Aogonek -40 KPX Ucircumflex Aring -40 KPX Ucircumflex Atilde -40 KPX Ucircumflex comma -25 KPX Ucircumflex period -25 KPX Udieresis A -40 KPX Udieresis Aacute -40 KPX Udieresis Abreve -40 KPX Udieresis Acircumflex -40 KPX Udieresis Adieresis -40 KPX Udieresis Agrave -40 KPX Udieresis Amacron -40 KPX Udieresis Aogonek -40 KPX Udieresis Aring -40 KPX Udieresis Atilde -40 KPX Udieresis comma -25 KPX Udieresis period -25 KPX Ugrave A -40 KPX Ugrave Aacute -40 KPX Ugrave Abreve -40 KPX Ugrave Acircumflex -40 KPX Ugrave Adieresis -40 KPX Ugrave Agrave -40 KPX Ugrave Amacron -40 KPX Ugrave Aogonek -40 KPX Ugrave Aring -40 KPX Ugrave Atilde -40 KPX Ugrave comma -25 KPX Ugrave period -25 KPX Uhungarumlaut A -40 KPX Uhungarumlaut Aacute -40 KPX Uhungarumlaut Abreve -40 KPX Uhungarumlaut Acircumflex -40 KPX Uhungarumlaut Adieresis -40 KPX Uhungarumlaut Agrave -40 KPX Uhungarumlaut Amacron -40 KPX Uhungarumlaut Aogonek -40 KPX Uhungarumlaut Aring -40 KPX Uhungarumlaut Atilde -40 KPX Uhungarumlaut comma -25 KPX Uhungarumlaut period -25 KPX Umacron A -40 KPX Umacron Aacute -40 KPX Umacron Abreve -40 KPX Umacron Acircumflex -40 KPX Umacron Adieresis -40 KPX Umacron Agrave -40 KPX Umacron Amacron -40 KPX Umacron Aogonek -40 KPX Umacron Aring -40 KPX Umacron Atilde -40 KPX Umacron comma -25 KPX Umacron period -25 KPX Uogonek A -40 KPX Uogonek Aacute -40 KPX Uogonek Abreve -40 KPX Uogonek Acircumflex -40 KPX Uogonek Adieresis -40 KPX Uogonek Agrave -40 KPX Uogonek Amacron -40 KPX Uogonek Aogonek -40 KPX Uogonek Aring -40 KPX Uogonek Atilde -40 KPX Uogonek comma -25 KPX Uogonek period -25 KPX Uring A -40 KPX Uring Aacute -40 KPX Uring Abreve -40 KPX Uring Acircumflex -40 KPX Uring Adieresis -40 KPX Uring Agrave -40 KPX Uring Amacron -40 KPX Uring Aogonek -40 KPX Uring Aring -40 KPX Uring Atilde -40 KPX Uring comma -25 KPX Uring period -25 KPX V A -60 KPX V Aacute -60 KPX V Abreve -60 KPX V Acircumflex -60 KPX V Adieresis -60 KPX V Agrave -60 KPX V Amacron -60 KPX V Aogonek -60 KPX V Aring -60 KPX V Atilde -60 KPX V O -30 KPX V Oacute -30 KPX V Ocircumflex -30 KPX V Odieresis -30 KPX V Ograve -30 KPX V Ohungarumlaut -30 KPX V Omacron -30 KPX V Oslash -30 KPX V Otilde -30 KPX V a -111 KPX V aacute -111 KPX V abreve -111 KPX V acircumflex -111 KPX V adieresis -111 KPX V agrave -111 KPX V amacron -111 KPX V aogonek -111 KPX V aring -111 KPX V atilde -111 KPX V colon -65 KPX V comma -129 KPX V e -111 KPX V eacute -111 KPX V ecaron -111 KPX V ecircumflex -111 KPX V edieresis -71 KPX V edotaccent -111 KPX V egrave -71 KPX V emacron -71 KPX V eogonek -111 KPX V hyphen -55 KPX V i -74 KPX V iacute -74 KPX V icircumflex -34 KPX V idieresis -34 KPX V igrave -34 KPX V imacron -34 KPX V iogonek -74 KPX V o -111 KPX V oacute -111 KPX V ocircumflex -111 KPX V odieresis -111 KPX V ograve -111 KPX V ohungarumlaut -111 KPX V omacron -111 KPX V oslash -111 KPX V otilde -111 KPX V period -129 KPX V semicolon -74 KPX V u -74 KPX V uacute -74 KPX V ucircumflex -74 KPX V udieresis -74 KPX V ugrave -74 KPX V uhungarumlaut -74 KPX V umacron -74 KPX V uogonek -74 KPX V uring -74 KPX W A -60 KPX W Aacute -60 KPX W Abreve -60 KPX W Acircumflex -60 KPX W Adieresis -60 KPX W Agrave -60 KPX W Amacron -60 KPX W Aogonek -60 KPX W Aring -60 KPX W Atilde -60 KPX W O -25 KPX W Oacute -25 KPX W Ocircumflex -25 KPX W Odieresis -25 KPX W Ograve -25 KPX W Ohungarumlaut -25 KPX W Omacron -25 KPX W Oslash -25 KPX W Otilde -25 KPX W a -92 KPX W aacute -92 KPX W abreve -92 KPX W acircumflex -92 KPX W adieresis -92 KPX W agrave -92 KPX W amacron -92 KPX W aogonek -92 KPX W aring -92 KPX W atilde -92 KPX W colon -65 KPX W comma -92 KPX W e -92 KPX W eacute -92 KPX W ecaron -92 KPX W ecircumflex -92 KPX W edieresis -52 KPX W edotaccent -92 KPX W egrave -52 KPX W emacron -52 KPX W eogonek -92 KPX W hyphen -37 KPX W i -55 KPX W iacute -55 KPX W iogonek -55 KPX W o -92 KPX W oacute -92 KPX W ocircumflex -92 KPX W odieresis -92 KPX W ograve -92 KPX W ohungarumlaut -92 KPX W omacron -92 KPX W oslash -92 KPX W otilde -92 KPX W period -92 KPX W semicolon -65 KPX W u -55 KPX W uacute -55 KPX W ucircumflex -55 KPX W udieresis -55 KPX W ugrave -55 KPX W uhungarumlaut -55 KPX W umacron -55 KPX W uogonek -55 KPX W uring -55 KPX W y -70 KPX W yacute -70 KPX W ydieresis -70 KPX Y A -50 KPX Y Aacute -50 KPX Y Abreve -50 KPX Y Acircumflex -50 KPX Y Adieresis -50 KPX Y Agrave -50 KPX Y Amacron -50 KPX Y Aogonek -50 KPX Y Aring -50 KPX Y Atilde -50 KPX Y O -15 KPX Y Oacute -15 KPX Y Ocircumflex -15 KPX Y Odieresis -15 KPX Y Ograve -15 KPX Y Ohungarumlaut -15 KPX Y Omacron -15 KPX Y Oslash -15 KPX Y Otilde -15 KPX Y a -92 KPX Y aacute -92 KPX Y abreve -92 KPX Y acircumflex -92 KPX Y adieresis -92 KPX Y agrave -92 KPX Y amacron -92 KPX Y aogonek -92 KPX Y aring -92 KPX Y atilde -92 KPX Y colon -65 KPX Y comma -92 KPX Y e -92 KPX Y eacute -92 KPX Y ecaron -92 KPX Y ecircumflex -92 KPX Y edieresis -52 KPX Y edotaccent -92 KPX Y egrave -52 KPX Y emacron -52 KPX Y eogonek -92 KPX Y hyphen -74 KPX Y i -74 KPX Y iacute -74 KPX Y icircumflex -34 KPX Y idieresis -34 KPX Y igrave -34 KPX Y imacron -34 KPX Y iogonek -74 KPX Y o -92 KPX Y oacute -92 KPX Y ocircumflex -92 KPX Y odieresis -92 KPX Y ograve -92 KPX Y ohungarumlaut -92 KPX Y omacron -92 KPX Y oslash -92 KPX Y otilde -92 KPX Y period -92 KPX Y semicolon -65 KPX Y u -92 KPX Y uacute -92 KPX Y ucircumflex -92 KPX Y udieresis -92 KPX Y ugrave -92 KPX Y uhungarumlaut -92 KPX Y umacron -92 KPX Y uogonek -92 KPX Y uring -92 KPX Yacute A -50 KPX Yacute Aacute -50 KPX Yacute Abreve -50 KPX Yacute Acircumflex -50 KPX Yacute Adieresis -50 KPX Yacute Agrave -50 KPX Yacute Amacron -50 KPX Yacute Aogonek -50 KPX Yacute Aring -50 KPX Yacute Atilde -50 KPX Yacute O -15 KPX Yacute Oacute -15 KPX Yacute Ocircumflex -15 KPX Yacute Odieresis -15 KPX Yacute Ograve -15 KPX Yacute Ohungarumlaut -15 KPX Yacute Omacron -15 KPX Yacute Oslash -15 KPX Yacute Otilde -15 KPX Yacute a -92 KPX Yacute aacute -92 KPX Yacute abreve -92 KPX Yacute acircumflex -92 KPX Yacute adieresis -92 KPX Yacute agrave -92 KPX Yacute amacron -92 KPX Yacute aogonek -92 KPX Yacute aring -92 KPX Yacute atilde -92 KPX Yacute colon -65 KPX Yacute comma -92 KPX Yacute e -92 KPX Yacute eacute -92 KPX Yacute ecaron -92 KPX Yacute ecircumflex -92 KPX Yacute edieresis -52 KPX Yacute edotaccent -92 KPX Yacute egrave -52 KPX Yacute emacron -52 KPX Yacute eogonek -92 KPX Yacute hyphen -74 KPX Yacute i -74 KPX Yacute iacute -74 KPX Yacute icircumflex -34 KPX Yacute idieresis -34 KPX Yacute igrave -34 KPX Yacute imacron -34 KPX Yacute iogonek -74 KPX Yacute o -92 KPX Yacute oacute -92 KPX Yacute ocircumflex -92 KPX Yacute odieresis -92 KPX Yacute ograve -92 KPX Yacute ohungarumlaut -92 KPX Yacute omacron -92 KPX Yacute oslash -92 KPX Yacute otilde -92 KPX Yacute period -92 KPX Yacute semicolon -65 KPX Yacute u -92 KPX Yacute uacute -92 KPX Yacute ucircumflex -92 KPX Yacute udieresis -92 KPX Yacute ugrave -92 KPX Yacute uhungarumlaut -92 KPX Yacute umacron -92 KPX Yacute uogonek -92 KPX Yacute uring -92 KPX Ydieresis A -50 KPX Ydieresis Aacute -50 KPX Ydieresis Abreve -50 KPX Ydieresis Acircumflex -50 KPX Ydieresis Adieresis -50 KPX Ydieresis Agrave -50 KPX Ydieresis Amacron -50 KPX Ydieresis Aogonek -50 KPX Ydieresis Aring -50 KPX Ydieresis Atilde -50 KPX Ydieresis O -15 KPX Ydieresis Oacute -15 KPX Ydieresis Ocircumflex -15 KPX Ydieresis Odieresis -15 KPX Ydieresis Ograve -15 KPX Ydieresis Ohungarumlaut -15 KPX Ydieresis Omacron -15 KPX Ydieresis Oslash -15 KPX Ydieresis Otilde -15 KPX Ydieresis a -92 KPX Ydieresis aacute -92 KPX Ydieresis abreve -92 KPX Ydieresis acircumflex -92 KPX Ydieresis adieresis -92 KPX Ydieresis agrave -92 KPX Ydieresis amacron -92 KPX Ydieresis aogonek -92 KPX Ydieresis aring -92 KPX Ydieresis atilde -92 KPX Ydieresis colon -65 KPX Ydieresis comma -92 KPX Ydieresis e -92 KPX Ydieresis eacute -92 KPX Ydieresis ecaron -92 KPX Ydieresis ecircumflex -92 KPX Ydieresis edieresis -52 KPX Ydieresis edotaccent -92 KPX Ydieresis egrave -52 KPX Ydieresis emacron -52 KPX Ydieresis eogonek -92 KPX Ydieresis hyphen -74 KPX Ydieresis i -74 KPX Ydieresis iacute -74 KPX Ydieresis icircumflex -34 KPX Ydieresis idieresis -34 KPX Ydieresis igrave -34 KPX Ydieresis imacron -34 KPX Ydieresis iogonek -74 KPX Ydieresis o -92 KPX Ydieresis oacute -92 KPX Ydieresis ocircumflex -92 KPX Ydieresis odieresis -92 KPX Ydieresis ograve -92 KPX Ydieresis ohungarumlaut -92 KPX Ydieresis omacron -92 KPX Ydieresis oslash -92 KPX Ydieresis otilde -92 KPX Ydieresis period -92 KPX Ydieresis semicolon -65 KPX Ydieresis u -92 KPX Ydieresis uacute -92 KPX Ydieresis ucircumflex -92 KPX Ydieresis udieresis -92 KPX Ydieresis ugrave -92 KPX Ydieresis uhungarumlaut -92 KPX Ydieresis umacron -92 KPX Ydieresis uogonek -92 KPX Ydieresis uring -92 KPX a g -10 KPX a gbreve -10 KPX a gcommaaccent -10 KPX aacute g -10 KPX aacute gbreve -10 KPX aacute gcommaaccent -10 KPX abreve g -10 KPX abreve gbreve -10 KPX abreve gcommaaccent -10 KPX acircumflex g -10 KPX acircumflex gbreve -10 KPX acircumflex gcommaaccent -10 KPX adieresis g -10 KPX adieresis gbreve -10 KPX adieresis gcommaaccent -10 KPX agrave g -10 KPX agrave gbreve -10 KPX agrave gcommaaccent -10 KPX amacron g -10 KPX amacron gbreve -10 KPX amacron gcommaaccent -10 KPX aogonek g -10 KPX aogonek gbreve -10 KPX aogonek gcommaaccent -10 KPX aring g -10 KPX aring gbreve -10 KPX aring gcommaaccent -10 KPX atilde g -10 KPX atilde gbreve -10 KPX atilde gcommaaccent -10 KPX b period -40 KPX b u -20 KPX b uacute -20 KPX b ucircumflex -20 KPX b udieresis -20 KPX b ugrave -20 KPX b uhungarumlaut -20 KPX b umacron -20 KPX b uogonek -20 KPX b uring -20 KPX c h -15 KPX c k -20 KPX c kcommaaccent -20 KPX cacute h -15 KPX cacute k -20 KPX cacute kcommaaccent -20 KPX ccaron h -15 KPX ccaron k -20 KPX ccaron kcommaaccent -20 KPX ccedilla h -15 KPX ccedilla k -20 KPX ccedilla kcommaaccent -20 KPX comma quotedblright -140 KPX comma quoteright -140 KPX e comma -10 KPX e g -40 KPX e gbreve -40 KPX e gcommaaccent -40 KPX e period -15 KPX e v -15 KPX e w -15 KPX e x -20 KPX e y -30 KPX e yacute -30 KPX e ydieresis -30 KPX eacute comma -10 KPX eacute g -40 KPX eacute gbreve -40 KPX eacute gcommaaccent -40 KPX eacute period -15 KPX eacute v -15 KPX eacute w -15 KPX eacute x -20 KPX eacute y -30 KPX eacute yacute -30 KPX eacute ydieresis -30 KPX ecaron comma -10 KPX ecaron g -40 KPX ecaron gbreve -40 KPX ecaron gcommaaccent -40 KPX ecaron period -15 KPX ecaron v -15 KPX ecaron w -15 KPX ecaron x -20 KPX ecaron y -30 KPX ecaron yacute -30 KPX ecaron ydieresis -30 KPX ecircumflex comma -10 KPX ecircumflex g -40 KPX ecircumflex gbreve -40 KPX ecircumflex gcommaaccent -40 KPX ecircumflex period -15 KPX ecircumflex v -15 KPX ecircumflex w -15 KPX ecircumflex x -20 KPX ecircumflex y -30 KPX ecircumflex yacute -30 KPX ecircumflex ydieresis -30 KPX edieresis comma -10 KPX edieresis g -40 KPX edieresis gbreve -40 KPX edieresis gcommaaccent -40 KPX edieresis period -15 KPX edieresis v -15 KPX edieresis w -15 KPX edieresis x -20 KPX edieresis y -30 KPX edieresis yacute -30 KPX edieresis ydieresis -30 KPX edotaccent comma -10 KPX edotaccent g -40 KPX edotaccent gbreve -40 KPX edotaccent gcommaaccent -40 KPX edotaccent period -15 KPX edotaccent v -15 KPX edotaccent w -15 KPX edotaccent x -20 KPX edotaccent y -30 KPX edotaccent yacute -30 KPX edotaccent ydieresis -30 KPX egrave comma -10 KPX egrave g -40 KPX egrave gbreve -40 KPX egrave gcommaaccent -40 KPX egrave period -15 KPX egrave v -15 KPX egrave w -15 KPX egrave x -20 KPX egrave y -30 KPX egrave yacute -30 KPX egrave ydieresis -30 KPX emacron comma -10 KPX emacron g -40 KPX emacron gbreve -40 KPX emacron gcommaaccent -40 KPX emacron period -15 KPX emacron v -15 KPX emacron w -15 KPX emacron x -20 KPX emacron y -30 KPX emacron yacute -30 KPX emacron ydieresis -30 KPX eogonek comma -10 KPX eogonek g -40 KPX eogonek gbreve -40 KPX eogonek gcommaaccent -40 KPX eogonek period -15 KPX eogonek v -15 KPX eogonek w -15 KPX eogonek x -20 KPX eogonek y -30 KPX eogonek yacute -30 KPX eogonek ydieresis -30 KPX f comma -10 KPX f dotlessi -60 KPX f f -18 KPX f i -20 KPX f iogonek -20 KPX f period -15 KPX f quoteright 92 KPX g comma -10 KPX g e -10 KPX g eacute -10 KPX g ecaron -10 KPX g ecircumflex -10 KPX g edieresis -10 KPX g edotaccent -10 KPX g egrave -10 KPX g emacron -10 KPX g eogonek -10 KPX g g -10 KPX g gbreve -10 KPX g gcommaaccent -10 KPX g period -15 KPX gbreve comma -10 KPX gbreve e -10 KPX gbreve eacute -10 KPX gbreve ecaron -10 KPX gbreve ecircumflex -10 KPX gbreve edieresis -10 KPX gbreve edotaccent -10 KPX gbreve egrave -10 KPX gbreve emacron -10 KPX gbreve eogonek -10 KPX gbreve g -10 KPX gbreve gbreve -10 KPX gbreve gcommaaccent -10 KPX gbreve period -15 KPX gcommaaccent comma -10 KPX gcommaaccent e -10 KPX gcommaaccent eacute -10 KPX gcommaaccent ecaron -10 KPX gcommaaccent ecircumflex -10 KPX gcommaaccent edieresis -10 KPX gcommaaccent edotaccent -10 KPX gcommaaccent egrave -10 KPX gcommaaccent emacron -10 KPX gcommaaccent eogonek -10 KPX gcommaaccent g -10 KPX gcommaaccent gbreve -10 KPX gcommaaccent gcommaaccent -10 KPX gcommaaccent period -15 KPX k e -10 KPX k eacute -10 KPX k ecaron -10 KPX k ecircumflex -10 KPX k edieresis -10 KPX k edotaccent -10 KPX k egrave -10 KPX k emacron -10 KPX k eogonek -10 KPX k o -10 KPX k oacute -10 KPX k ocircumflex -10 KPX k odieresis -10 KPX k ograve -10 KPX k ohungarumlaut -10 KPX k omacron -10 KPX k oslash -10 KPX k otilde -10 KPX k y -10 KPX k yacute -10 KPX k ydieresis -10 KPX kcommaaccent e -10 KPX kcommaaccent eacute -10 KPX kcommaaccent ecaron -10 KPX kcommaaccent ecircumflex -10 KPX kcommaaccent edieresis -10 KPX kcommaaccent edotaccent -10 KPX kcommaaccent egrave -10 KPX kcommaaccent emacron -10 KPX kcommaaccent eogonek -10 KPX kcommaaccent o -10 KPX kcommaaccent oacute -10 KPX kcommaaccent ocircumflex -10 KPX kcommaaccent odieresis -10 KPX kcommaaccent ograve -10 KPX kcommaaccent ohungarumlaut -10 KPX kcommaaccent omacron -10 KPX kcommaaccent oslash -10 KPX kcommaaccent otilde -10 KPX kcommaaccent y -10 KPX kcommaaccent yacute -10 KPX kcommaaccent ydieresis -10 KPX n v -40 KPX nacute v -40 KPX ncaron v -40 KPX ncommaaccent v -40 KPX ntilde v -40 KPX o g -10 KPX o gbreve -10 KPX o gcommaaccent -10 KPX o v -10 KPX oacute g -10 KPX oacute gbreve -10 KPX oacute gcommaaccent -10 KPX oacute v -10 KPX ocircumflex g -10 KPX ocircumflex gbreve -10 KPX ocircumflex gcommaaccent -10 KPX ocircumflex v -10 KPX odieresis g -10 KPX odieresis gbreve -10 KPX odieresis gcommaaccent -10 KPX odieresis v -10 KPX ograve g -10 KPX ograve gbreve -10 KPX ograve gcommaaccent -10 KPX ograve v -10 KPX ohungarumlaut g -10 KPX ohungarumlaut gbreve -10 KPX ohungarumlaut gcommaaccent -10 KPX ohungarumlaut v -10 KPX omacron g -10 KPX omacron gbreve -10 KPX omacron gcommaaccent -10 KPX omacron v -10 KPX oslash g -10 KPX oslash gbreve -10 KPX oslash gcommaaccent -10 KPX oslash v -10 KPX otilde g -10 KPX otilde gbreve -10 KPX otilde gcommaaccent -10 KPX otilde v -10 KPX period quotedblright -140 KPX period quoteright -140 KPX quoteleft quoteleft -111 KPX quoteright d -25 KPX quoteright dcroat -25 KPX quoteright quoteright -111 KPX quoteright r -25 KPX quoteright racute -25 KPX quoteright rcaron -25 KPX quoteright rcommaaccent -25 KPX quoteright s -40 KPX quoteright sacute -40 KPX quoteright scaron -40 KPX quoteright scedilla -40 KPX quoteright scommaaccent -40 KPX quoteright space -111 KPX quoteright t -30 KPX quoteright tcommaaccent -30 KPX quoteright v -10 KPX r a -15 KPX r aacute -15 KPX r abreve -15 KPX r acircumflex -15 KPX r adieresis -15 KPX r agrave -15 KPX r amacron -15 KPX r aogonek -15 KPX r aring -15 KPX r atilde -15 KPX r c -37 KPX r cacute -37 KPX r ccaron -37 KPX r ccedilla -37 KPX r comma -111 KPX r d -37 KPX r dcroat -37 KPX r e -37 KPX r eacute -37 KPX r ecaron -37 KPX r ecircumflex -37 KPX r edieresis -37 KPX r edotaccent -37 KPX r egrave -37 KPX r emacron -37 KPX r eogonek -37 KPX r g -37 KPX r gbreve -37 KPX r gcommaaccent -37 KPX r hyphen -20 KPX r o -45 KPX r oacute -45 KPX r ocircumflex -45 KPX r odieresis -45 KPX r ograve -45 KPX r ohungarumlaut -45 KPX r omacron -45 KPX r oslash -45 KPX r otilde -45 KPX r period -111 KPX r q -37 KPX r s -10 KPX r sacute -10 KPX r scaron -10 KPX r scedilla -10 KPX r scommaaccent -10 KPX racute a -15 KPX racute aacute -15 KPX racute abreve -15 KPX racute acircumflex -15 KPX racute adieresis -15 KPX racute agrave -15 KPX racute amacron -15 KPX racute aogonek -15 KPX racute aring -15 KPX racute atilde -15 KPX racute c -37 KPX racute cacute -37 KPX racute ccaron -37 KPX racute ccedilla -37 KPX racute comma -111 KPX racute d -37 KPX racute dcroat -37 KPX racute e -37 KPX racute eacute -37 KPX racute ecaron -37 KPX racute ecircumflex -37 KPX racute edieresis -37 KPX racute edotaccent -37 KPX racute egrave -37 KPX racute emacron -37 KPX racute eogonek -37 KPX racute g -37 KPX racute gbreve -37 KPX racute gcommaaccent -37 KPX racute hyphen -20 KPX racute o -45 KPX racute oacute -45 KPX racute ocircumflex -45 KPX racute odieresis -45 KPX racute ograve -45 KPX racute ohungarumlaut -45 KPX racute omacron -45 KPX racute oslash -45 KPX racute otilde -45 KPX racute period -111 KPX racute q -37 KPX racute s -10 KPX racute sacute -10 KPX racute scaron -10 KPX racute scedilla -10 KPX racute scommaaccent -10 KPX rcaron a -15 KPX rcaron aacute -15 KPX rcaron abreve -15 KPX rcaron acircumflex -15 KPX rcaron adieresis -15 KPX rcaron agrave -15 KPX rcaron amacron -15 KPX rcaron aogonek -15 KPX rcaron aring -15 KPX rcaron atilde -15 KPX rcaron c -37 KPX rcaron cacute -37 KPX rcaron ccaron -37 KPX rcaron ccedilla -37 KPX rcaron comma -111 KPX rcaron d -37 KPX rcaron dcroat -37 KPX rcaron e -37 KPX rcaron eacute -37 KPX rcaron ecaron -37 KPX rcaron ecircumflex -37 KPX rcaron edieresis -37 KPX rcaron edotaccent -37 KPX rcaron egrave -37 KPX rcaron emacron -37 KPX rcaron eogonek -37 KPX rcaron g -37 KPX rcaron gbreve -37 KPX rcaron gcommaaccent -37 KPX rcaron hyphen -20 KPX rcaron o -45 KPX rcaron oacute -45 KPX rcaron ocircumflex -45 KPX rcaron odieresis -45 KPX rcaron ograve -45 KPX rcaron ohungarumlaut -45 KPX rcaron omacron -45 KPX rcaron oslash -45 KPX rcaron otilde -45 KPX rcaron period -111 KPX rcaron q -37 KPX rcaron s -10 KPX rcaron sacute -10 KPX rcaron scaron -10 KPX rcaron scedilla -10 KPX rcaron scommaaccent -10 KPX rcommaaccent a -15 KPX rcommaaccent aacute -15 KPX rcommaaccent abreve -15 KPX rcommaaccent acircumflex -15 KPX rcommaaccent adieresis -15 KPX rcommaaccent agrave -15 KPX rcommaaccent amacron -15 KPX rcommaaccent aogonek -15 KPX rcommaaccent aring -15 KPX rcommaaccent atilde -15 KPX rcommaaccent c -37 KPX rcommaaccent cacute -37 KPX rcommaaccent ccaron -37 KPX rcommaaccent ccedilla -37 KPX rcommaaccent comma -111 KPX rcommaaccent d -37 KPX rcommaaccent dcroat -37 KPX rcommaaccent e -37 KPX rcommaaccent eacute -37 KPX rcommaaccent ecaron -37 KPX rcommaaccent ecircumflex -37 KPX rcommaaccent edieresis -37 KPX rcommaaccent edotaccent -37 KPX rcommaaccent egrave -37 KPX rcommaaccent emacron -37 KPX rcommaaccent eogonek -37 KPX rcommaaccent g -37 KPX rcommaaccent gbreve -37 KPX rcommaaccent gcommaaccent -37 KPX rcommaaccent hyphen -20 KPX rcommaaccent o -45 KPX rcommaaccent oacute -45 KPX rcommaaccent ocircumflex -45 KPX rcommaaccent odieresis -45 KPX rcommaaccent ograve -45 KPX rcommaaccent ohungarumlaut -45 KPX rcommaaccent omacron -45 KPX rcommaaccent oslash -45 KPX rcommaaccent otilde -45 KPX rcommaaccent period -111 KPX rcommaaccent q -37 KPX rcommaaccent s -10 KPX rcommaaccent sacute -10 KPX rcommaaccent scaron -10 KPX rcommaaccent scedilla -10 KPX rcommaaccent scommaaccent -10 KPX space A -18 KPX space Aacute -18 KPX space Abreve -18 KPX space Acircumflex -18 KPX space Adieresis -18 KPX space Agrave -18 KPX space Amacron -18 KPX space Aogonek -18 KPX space Aring -18 KPX space Atilde -18 KPX space T -18 KPX space Tcaron -18 KPX space Tcommaaccent -18 KPX space V -35 KPX space W -40 KPX space Y -75 KPX space Yacute -75 KPX space Ydieresis -75 KPX v comma -74 KPX v period -74 KPX w comma -74 KPX w period -74 KPX y comma -55 KPX y period -55 KPX yacute comma -55 KPX yacute period -55 KPX ydieresis comma -55 KPX ydieresis period -55 EndKernPairs EndKernData EndFontMetrics dompdf/lib/fonts/DejaVuSans.ttf 0000604 00002706524 15173213355 0012457 0 ustar 00 @FFTMs��� L GDEF�ώ� h �GPOSV��5 � ��GSUB��@Y �� �MATH�28} �h >OS/2Y-v- �� Vcmap�I&