Version Description
- Libraries: Updated dompdf to 2.0.0, addressing security vulnerabilities and introducing some changes that could possibly break high level customized setups (more information)
- Fix: respect custom (filtered) woocommerce template paths
- Fix: Check if the invoice is allowed before the shortcode output
- UI: Link to Professional extension for packing slip attachments
Download this release
Release Info
Developer | wpovernight |
Plugin | WooCommerce PDF Invoices & Packing Slips |
Version | 3.0.0 |
Comparing to | |
See all releases |
Code changes from version 2.16.0 to 3.0.0
- assets/css/settings-styles.css +5 -1
- assets/css/settings-styles.min.css +1 -1
- composer.json +1 -1
- composer.lock +77 -7
- includes/class-wcpdf-admin.php +1 -1
- includes/class-wcpdf-font-synchronizer.php +13 -10
- includes/class-wcpdf-frontend.php +5 -0
- includes/class-wcpdf-install.php +10 -1
- includes/class-wcpdf-pdf-maker.php +8 -10
- includes/class-wcpdf-settings-callbacks.php +13 -1
- includes/class-wcpdf-settings-debug.php +0 -11
- includes/class-wcpdf-settings-general.php +9 -10
- includes/class-wcpdf-settings.php +42 -10
- includes/documents/class-wcpdf-packing-slip.php +27 -2
- readme.txt +7 -1
- vendor/composer/autoload_classmap.php +0 -5
- vendor/composer/autoload_psr4.php +1 -0
- vendor/composer/autoload_static.php +8 -5
- vendor/composer/installed.json +85 -12
- vendor/composer/installed.php +15 -6
- vendor/dompdf/dompdf/README.md +0 -6
- vendor/dompdf/dompdf/VERSION +1 -1
- vendor/dompdf/dompdf/composer.json +1 -0
- vendor/dompdf/dompdf/lib/Cpdf.php +178 -170
- vendor/dompdf/dompdf/lib/fonts/dompdf_font_family_cache.dist.php +0 -97
- vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json +80 -0
- vendor/dompdf/dompdf/lib/html5lib/Data.php +0 -123
- vendor/dompdf/dompdf/lib/html5lib/InputStream.php +0 -299
- vendor/dompdf/dompdf/lib/html5lib/Parser.php +0 -37
- vendor/dompdf/dompdf/lib/html5lib/Tokenizer.php +0 -2470
- vendor/dompdf/dompdf/lib/html5lib/TreeBuilder.php +0 -3989
- vendor/dompdf/dompdf/lib/html5lib/named-character-references.ser +0 -1
- vendor/dompdf/dompdf/lib/res/html.css +1 -1
- vendor/dompdf/dompdf/src/Adapter/CPDF.php +159 -415
- vendor/dompdf/dompdf/src/Adapter/GD.php +155 -333
- vendor/dompdf/dompdf/src/Adapter/PDFLib.php +145 -374
- vendor/dompdf/dompdf/src/Canvas.php +153 -145
- vendor/dompdf/dompdf/src/Cellmap.php +44 -54
- vendor/dompdf/dompdf/src/Css/Color.php +4 -4
- vendor/dompdf/dompdf/src/Css/Style.php +1431 -1324
- vendor/dompdf/dompdf/src/Css/Stylesheet.php +44 -81
- vendor/dompdf/dompdf/src/Dompdf.php +124 -148
- vendor/dompdf/dompdf/src/FontMetrics.php +145 -125
- vendor/dompdf/dompdf/src/Frame.php +29 -81
- vendor/dompdf/dompdf/src/Frame/Factory.php +2 -2
- vendor/dompdf/dompdf/src/Frame/FrameList.php +0 -35
- vendor/dompdf/dompdf/src/Frame/FrameTree.php +16 -4
- vendor/dompdf/dompdf/src/Frame/FrameTreeIterator.php +1 -4
- vendor/dompdf/dompdf/src/Frame/FrameTreeList.php +0 -35
- vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php +62 -37
- vendor/dompdf/dompdf/src/FrameDecorator/Block.php +4 -4
- vendor/dompdf/dompdf/src/FrameDecorator/Image.php +9 -3
- vendor/dompdf/dompdf/src/FrameDecorator/Inline.php +13 -11
- vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php +0 -15
- vendor/dompdf/dompdf/src/FrameDecorator/Page.php +4 -6
- vendor/dompdf/dompdf/src/FrameDecorator/Table.php +9 -86
- vendor/dompdf/dompdf/src/FrameDecorator/TableCell.php +1 -1
- vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php +0 -29
- vendor/dompdf/dompdf/src/FrameDecorator/Text.php +18 -20
- vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php +34 -36
- vendor/dompdf/dompdf/src/FrameReflower/Block.php +17 -16
- vendor/dompdf/dompdf/src/FrameReflower/Image.php +6 -6
- vendor/dompdf/dompdf/src/FrameReflower/Inline.php +11 -7
- vendor/dompdf/dompdf/src/FrameReflower/ListBullet.php +1 -1
- vendor/dompdf/dompdf/src/FrameReflower/Page.php +5 -8
- vendor/dompdf/dompdf/src/FrameReflower/Table.php +28 -30
- vendor/dompdf/dompdf/src/FrameReflower/TableCell.php +4 -2
- vendor/dompdf/dompdf/src/FrameReflower/TableRow.php +2 -2
- vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php +2 -2
- vendor/dompdf/dompdf/src/FrameReflower/Text.php +26 -29
- vendor/dompdf/dompdf/src/Helpers.php +107 -47
- vendor/dompdf/dompdf/src/Image/Cache.php +93 -104
- vendor/dompdf/dompdf/src/LineBox.php +3 -3
- vendor/dompdf/dompdf/src/Options.php +23 -7
assets/css/settings-styles.css
CHANGED
@@ -392,6 +392,10 @@ body.woocommerce_page_wpo_wcpdf_options_page {
|
|
392 |
border-left: 4px solid #51266b;
|
393 |
}
|
394 |
|
|
|
|
|
|
|
|
|
395 |
#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar {
|
396 |
flex: 0 0 100%;
|
397 |
}
|
@@ -901,4 +905,4 @@ body.woocommerce_page_wpo_wcpdf_options_page {
|
|
901 |
#wpo-wcpdf-preview-wrapper .slider.slide-left:after {
|
902 |
right: 0;
|
903 |
}
|
904 |
-
}
|
392 |
border-left: 4px solid #51266b;
|
393 |
}
|
394 |
|
395 |
+
#wpo-wcpdf-settings .notice-info.inline {
|
396 |
+
border-left-color: #51266b;
|
397 |
+
}
|
398 |
+
|
399 |
#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar {
|
400 |
flex: 0 0 100%;
|
401 |
}
|
905 |
#wpo-wcpdf-preview-wrapper .slider.slide-left:after {
|
906 |
right: 0;
|
907 |
}
|
908 |
+
}
|
assets/css/settings-styles.min.css
CHANGED
@@ -1 +1 @@
|
|
1 |
-
span.wpo-warning{display:inline-block;border:1px solid red;border-left:4px solid red;padding:5px 15px;background-color:#fff}.wcpdf-extensions-ad{position:relative;min-height:90px;border:1px solid #3d5c99;background-color:#ebf5ff;padding:15px;padding-left:100px;margin-top:15px}img.wpo-helper{position:absolute;bottom:0;left:3px}.wcpdf-extensions-ad h3{margin:0}.wcpdf-extensions-ad ul{margin:0;margin-left:1.5em}.wcpdf-extensions li{margin:0}.wcpdf-extensions li ul{list-style-type:square;margin-top:.5em;margin-bottom:.5em}.wcpdf-extensions>li:before{content:"";border-color:transparent transparent transparent #111;border-style:solid;border-width:.35em .35em .35em .45em;display:block;height:0;width:0;left:-1em;top:.9em;position:relative}.wcpdf-extensions li:not(.expanded){cursor:pointer}.wcpdf-extensions .expanded:before{border-color:#111 transparent transparent transparent;left:-1.17em;border-width:.45em .45em .35em .35em!important}.wcpdf-extensions .more{padding:10px;background-color:#fff;border:1px solid #ccc;border-radius:5px}.wcpdf-extensions table td{vertical-align:top}.dropbox-logo{margin-bottom:-10px;margin-right:10px}.cloud-logo{margin-bottom:-10px;margin-top:-5px;margin-right:10px}#img-header_logo{max-height:200px;width:auto;max-width:100%}.multiple-text-input label{min-width:120px;display:inline-block}table.wcpdf_documents_settings_list{width:100%;border-collapse:collapse;border-spacing:0;background-color:#fff;border-top:2px solid #000}table.wcpdf_documents_settings_list tr.odd{background-color:#ebf5ff}table.wcpdf_documents_settings_list td{padding:5px}table.wcpdf_documents_settings_list a{text-decoration:none}table.wcpdf_documents_settings_list td.settings-icon{text-align:right}table.wcpdf_documents_settings_list td.title{font-weight:700}.wcpdf-settings-sections ul{height:3em}.wcpdf-settings-sections ul li{float:left;margin-right:10px}.wcpdf-settings-sections ul li a{text-decoration:none;display:inline-block;padding:.8em 1em;color:#50575e;border:1px solid #c3c4c7;box-sizing:border-box}.wcpdf-settings-sections ul li a.active{border:2px solid #51266b;padding:calc(.8em - 1px) calc(1em - 1px);color:#000}.wcpdf_document_settings_sections{margin-bottom:40px;position:relative}.wcpdf_document_settings_sections>h2{cursor:pointer;padding:1em .8em;margin:0;border:1px solid #eaeaea}.wcpdf_document_settings_sections ul{background:#fff;list-style:none;margin:0;padding:0;width:100%;display:block;height:auto;display:none;box-sizing:border-box;position:absolute;border-left:1px solid #eaeaea;border-right:1px solid #eaeaea;z-index:1000;box-shadow:0 35px 35px -8px rgba(0,0,0,.1);-webkit-box-shadow:0 35px 35px -8px rgba(0,0,0,.1)}.wcpdf_document_settings_sections ul.active{display:block}.wcpdf_document_settings_sections ul li{box-sizing:border-box;padding:0;margin-bottom:0;border-bottom:1px solid #eaeaea;font-size:1.1em}.wcpdf_document_settings_sections ul li:hover{cursor:pointer;background:#51266b;color:#fff}.wcpdf_document_settings_sections ul li:hover a{color:#fff}.wcpdf_document_settings_sections ul li a{color:#000;text-decoration:none;padding:1.2em 1.6em;display:block}.wcpdf_document_settings_sections .arrow-down{font-size:.7em;color:#999;margin-left:8px;font-weight:400;float:right}.wcpdf_document_settings_sections p:hover,.wcpdf_document_settings_sections p:hover>.arrow-down{color:#222}.edit-next-number{opacity:.5}.edit-next-number:hover{opacity:1;cursor:pointer}body.woocommerce_page_wpo_wcpdf_options_page{background:#fff}.wrap [class$=icon32]+h2{font-size:18px;padding:1em}.wrap .notice{margin:15px 0 0}.nav-tab-wrapper a.nav-tab{background:0 0;border:none;border-bottom:3px solid transparent;padding:1em 0;margin:0 1.2em;font-size:15px}.nav-tab-wrapper a.nav-tab.nav-tab-active{border-bottom:3px solid #51266b}#wpo-wcpdf-preview-wrapper{width:100%;height:auto;position:relative;display:flex;align-items:flex-start}#wpo-wcpdf-preview-wrapper .preview-document,#wpo-wcpdf-preview-wrapper .sidebar{transition:.3s ease-in-out}#wpo-wcpdf-preview-wrapper .sidebar{height:auto;padding:4em 0 0 0;box-sizing:border-box;background:0 0;flex:0 0 35%;overflow-x:hidden}#wpo-wcpdf-preview-wrapper .sidebar>form{background:#fff;overflow:visible;padding:0;margin-left:2em;box-sizing:border-box;width:calc(100% - 4em);max-width:50vw}#wpo-wcpdf-preview-wrapper .sidebar>form.editor{max-width:none}#wpo-wcpdf-preview-wrapper .sidebar .form-table,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>th{display:block;width:100%;padding:0}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>th{padding-bottom:.6em}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td{padding-bottom:2.4em}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td p.description,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td>p.description{font-size:.85em;padding-top:.7em}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td>input[type=text],#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td>select,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td>textarea,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td>input[type=text],#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td>select,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td>textarea{max-width:none;width:100%}#wpo-wcpdf-preview-wrapper input[type=text][size]{width:auto!important;max-width:100%!important}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td>input#next_invoice_number{width:auto!important}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table{display:table}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table tbody,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table tbody,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table tbody{display:table-row-group}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table tbody tr,#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table tr,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table tbody tr,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table tr,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table tbody tr,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table tr{display:table-row}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table>tbody>tr>th{display:table-cell;padding:15px 10px 15px 0;width:auto}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table>tbody>tr>th{width:300px!important}#wpo-wcpdf-settings .form-table .ui-tabs-nav{padding-left:0!important;margin-left:0!important}#wpo-wcpdf-settings .translations input,#wpo-wcpdf-settings .translations textarea{width:100%}#wpo-wcpdf-settings .wcpdf-attachment-settings-hint{border-left:4px solid #51266b}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar{flex:0 0 100%}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar{flex:0 0 95%}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=full] .sidebar{flex:0 0 95%;margin-left:-95%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar{flex:0 0 95%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=sidebar] .sidebar{flex:0 0 35%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=full] .sidebar{margin-left:-35%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-from-preview-state=full] .sidebar{transition-delay:.4s}#wpo-wcpdf-preview-wrapper .preview-document{padding:0;box-sizing:border-box;position:sticky;top:2.4em;flex:0 0 60%}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .preview-document{display:none}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .preview-document{display:none}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=full] .preview-document{flex:0 0 95%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .preview-document{flex:0 0 60%;margin-right:-60%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=sidebar] .preview-document{flex:0 0 60%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=full] .preview-document{flex:0 0 95%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-from-preview-state=full] .preview-document{transition-delay:.4s}.preview-document .preview{width:100%;box-sizing:border-box;padding-right:5%}.preview-document .preview>#preview-canvas{display:block;max-width:800px;max-height:85vh;width:auto!important;margin:0 auto;background:#fff;box-shadow:0 0 35px -8px rgba(0,0,0,.12);-webkit-box-shadow:0 0 35px -8px rgba(0,0,0,.12)}#wpo-wcpdf-preview-wrapper[data-preview-states="2"] #preview-canvas{max-height:170vh}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-from-preview-state=sidebar] #preview-canvas{max-height:170vh;transition:max-height .4s ease-in-out .3s}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-from-preview-state=full] #preview-canvas{transition:max-height .4s ease-in-out 0s}.preview-document .preview-data-wrapper{width:100%;height:4em}.preview-document .preview-data-wrapper .preview-document-type,.preview-document .preview-data-wrapper .preview-order-data{float:right}.preview-document .preview-data-wrapper .preview-document-type{margin-right:30px}.preview-document .preview-data-wrapper .preview-document-type ul>li{text-decoration:none;color:initial;padding:1.4em 1.6em}.preview-document .preview-data-wrapper .preview-document-type ul>li:hover{color:#fff!important}.preview-document .preview-data-wrapper .save-settings{padding:1em 0 0 0;float:right;overflow:hidden;position:relative}.preview-document .preview-data-wrapper .save-settings p{padding:0;margin:0 0 0 2em;position:relative;margin-right:-200px;transition:margin-right .3s ease-out}.preview-document .preview-data-wrapper .save-settings p:after{content:'';display:block;pointer-events:none;position:absolute;box-sizing:border-box;border-radius:3px;right:0;top:0;background:0 0;width:100%;height:100%;z-index:10;border:0 solid #fff;animation:border-pulse 4s infinite}@keyframes border-pulse{0%{border-color:rgba(255,255,255,0);border-width:8px}50%{border-color:#fff;border-width:0}}.preview-document .preview-data-wrapper .save-settings p input:focus{outline-width:0;box-shadow:none}.preview-document .preview-data p{padding:1.4em 0;margin:0;color:#666;text-align:right;cursor:pointer;font-weight:lighter;float:right}.preview-document .preview-data p.order-search{display:none}.preview-document .preview-data input{float:right;margin:1em 0 0 1em;padding:.1em .5em;width:20ch;margin-right:-25ch;display:none}.preview-document .preview-data input.active{margin-right:0;display:inline-block}.preview-document .preview-data ul{position:absolute;right:0;top:4em;background:#fff;box-shadow:0 0 25px -10px rgba(0,0,0,.2);-webkit-box-shadow:0 0 25px -10px rgba(0,0,0,.2);list-style:none;margin:0;padding:0;min-width:24em;display:block;height:0;overflow:hidden}.preview-document .preview-data ul.active{height:auto;z-index:1}.preview-document .preview-data ul li{box-sizing:border-box;padding:0;margin-bottom:0;border-bottom:1px solid #eaeaea;font-size:1.1em}.preview-document .preview-data ul li:hover{cursor:pointer;background:#51266b;color:#fff}.preview-document .preview-data ul li a,.preview-document .preview-data.preview-order-data ul li{display:block;padding:1.4em 1.6em}.preview-document .preview-data .arrow-down{font-size:.8em;color:#999;margin-left:8px}.preview-document .preview-data p:hover,.preview-document .preview-data p:hover>.arrow-down{color:#222}.preview-document .preview-data #preview-order-search-results{display:none;position:absolute;right:0;top:4em;width:300px;box-shadow:0 0 25px -10px rgba(0,0,0,.2);-webkit-box-shadow:0 0 25px -10px rgba(0,0,0,.2);padding:20px 0;background-color:#fff}.preview-document .preview-data #preview-order-search-results a{display:block;border-left:1px solid #999;border-right:1px solid #999;border-top:1px solid #999;color:#000;padding:10px;margin:0 20px;text-decoration:none;cursor:pointer}.preview-document .preview-data #preview-order-search-results a:last-child{border-bottom:1px solid #999}.preview-document .preview-data #preview-order-search-results a:hover{background-color:#51266b;color:#fff}.preview-document .preview-data #preview-order-search-results .order-number{font-weight:700}.preview-document .preview-data #preview-order-search-results .date,.preview-document .preview-data #preview-order-search-results .total{margin-top:6px;display:inline-block}.preview-document .preview-data #preview-order-search-results .total{float:right}.preview-document .preview-data #preview-order-search-results .error{margin:0 20px}.preview-document .preview-order-search-wrapper{position:relative;float:right}.preview-document .preview-order-search-wrapper img.preview-order-search-clear{position:absolute;width:30px;height:16px;top:22px;right:6px;display:none;cursor:pointer}#wpo-wcpdf-preview-wrapper .gutter{flex:0 0 5%;position:sticky;top:2.4em;height:170vh}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .gutter .slide-left,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .gutter .slide-left{float:right}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=sidebar] .gutter .slide-left{border:none}#wpo-wcpdf-preview-wrapper .slider{box-sizing:border-box;padding-top:2.4em;color:#999;font-weight:700;cursor:pointer;font-size:.7em;line-height:1em;width:50%;height:100%;float:left}#wpo-wcpdf-preview-wrapper .slider.slide-left{text-align:right;padding-right:10px;border-right:1px solid #ccc}#wpo-wcpdf-preview-wrapper .slider.slide-right{text-align:left;padding-left:10px;border-left:1px solid #ccc;display:none}#wpo-wcpdf-preview-wrapper .gutter-arrow{width:0;height:0;border-top:3px solid transparent;border-bottom:3px solid transparent;display:block}#wpo-wcpdf-preview-wrapper .arrow-left{border-right:7px solid #999;float:right}#wpo-wcpdf-preview-wrapper .arrow-right{border-left:7px solid #999}#wpo-wcpdf-preview-wrapper .slider:hover>.arrow-left{border-right:7px solid #222}#wpo-wcpdf-preview-wrapper .slider:hover>.arrow-right{border-left:7px solid #222}#wpo-wcpdf-preview-wrapper .slider.slide-left:after{content:'Preview';position:absolute;top:1.55em;right:2em;font-size:1.4em;display:none}#wpo-wcpdf-preview-wrapper .slider.slide-right:after{content:'Settings';position:absolute;top:1.55em;left:2em;font-size:1.4em;display:none}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .gutter{display:none}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=sidebar] .gutter{height:100vh}#wpo-wcpdf-preview-wrapper[data-preview-state=full] .slide-right:after{display:inline-block}#wpo-wcpdf-preview-wrapper[data-preview-state=closed] .slide-left:after{display:inline-block}#wpo-wcpdf-preview-wrapper.static .gutter,#wpo-wcpdf-preview-wrapper.static .preview-document{position:static!important}#wpo-wcpdf-preview-wrapper.static .sidebar{height:170vh!important;overflow:hidden}#wpo-wcpdf-preview-wrapper input.readonly,#wpo-wcpdf-preview-wrapper input[readonly],#wpo-wcpdf-preview-wrapper textarea.readonly,#wpo-wcpdf-preview-wrapper textarea[readonly]{background-color:#f8f8f8}#wpo-wcpdf-preview-wrapper[data-preview-state=sidebar] .select2.select2-container{width:100%!important}@media screen and (min-width:1920px){.preview-document .preview>#preview-canvas{max-width:900px}}@media screen and (max-width:1200px){.preview-document .preview>#preview-canvas{max-width:680px}.nav-tab-wrapper a.nav-tab{padding:1em 2em;margin:0 .5em .5em 0;border:1px solid #ccc;box-sizing:border-box;height:4em}.nav-tab-wrapper a.nav-tab.nav-tab-active{border:3px solid #51266b}}@media screen and (max-width:960px){.preview-document .preview>#preview-canvas{width:80vw!important}#wpo-wcpdf-preview-wrapper .sidebar>form{max-width:100%}#wpo-wcpdf-preview-wrapper[data-preview-state=closed] .select2.select2-container{width:100%!important}#wpo-wcpdf-preview-wrapper .sidebar .form-table,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>th{display:block!important}.preview-document .preview-data-wrapper{height:6em}.preview-document .preview-data p{padding:2.2em 0}#wpo-wcpdf-preview-wrapper .slider.slide-left:after,#wpo-wcpdf-preview-wrapper .slider.slide-right:after{top:1.5em;padding:1em;background:#fff;border:1px solid #ccc}#wpo-wcpdf-preview-wrapper .slider.slide-right:after{left:0}#wpo-wcpdf-preview-wrapper .slider.slide-left:after{right:0}}
|
1 |
+
span.wpo-warning{display:inline-block;border:1px solid red;border-left:4px solid red;padding:5px 15px;background-color:#fff}.wcpdf-extensions-ad{position:relative;min-height:90px;border:1px solid #3d5c99;background-color:#ebf5ff;padding:15px;padding-left:100px;margin-top:15px}img.wpo-helper{position:absolute;bottom:0;left:3px}.wcpdf-extensions-ad h3{margin:0}.wcpdf-extensions-ad ul{margin:0;margin-left:1.5em}.wcpdf-extensions li{margin:0}.wcpdf-extensions li ul{list-style-type:square;margin-top:.5em;margin-bottom:.5em}.wcpdf-extensions>li:before{content:"";border-color:transparent transparent transparent #111;border-style:solid;border-width:.35em .35em .35em .45em;display:block;height:0;width:0;left:-1em;top:.9em;position:relative}.wcpdf-extensions li:not(.expanded){cursor:pointer}.wcpdf-extensions .expanded:before{border-color:#111 transparent transparent transparent;left:-1.17em;border-width:.45em .45em .35em .35em!important}.wcpdf-extensions .more{padding:10px;background-color:#fff;border:1px solid #ccc;border-radius:5px}.wcpdf-extensions table td{vertical-align:top}.dropbox-logo{margin-bottom:-10px;margin-right:10px}.cloud-logo{margin-bottom:-10px;margin-top:-5px;margin-right:10px}#img-header_logo{max-height:200px;width:auto;max-width:100%}.multiple-text-input label{min-width:120px;display:inline-block}table.wcpdf_documents_settings_list{width:100%;border-collapse:collapse;border-spacing:0;background-color:#fff;border-top:2px solid #000}table.wcpdf_documents_settings_list tr.odd{background-color:#ebf5ff}table.wcpdf_documents_settings_list td{padding:5px}table.wcpdf_documents_settings_list a{text-decoration:none}table.wcpdf_documents_settings_list td.settings-icon{text-align:right}table.wcpdf_documents_settings_list td.title{font-weight:700}.wcpdf-settings-sections ul{height:3em}.wcpdf-settings-sections ul li{float:left;margin-right:10px}.wcpdf-settings-sections ul li a{text-decoration:none;display:inline-block;padding:.8em 1em;color:#50575e;border:1px solid #c3c4c7;box-sizing:border-box}.wcpdf-settings-sections ul li a.active{border:2px solid #51266b;padding:calc(.8em - 1px) calc(1em - 1px);color:#000}.wcpdf_document_settings_sections{margin-bottom:40px;position:relative}.wcpdf_document_settings_sections>h2{cursor:pointer;padding:1em .8em;margin:0;border:1px solid #eaeaea}.wcpdf_document_settings_sections ul{background:#fff;list-style:none;margin:0;padding:0;width:100%;display:block;height:auto;display:none;box-sizing:border-box;position:absolute;border-left:1px solid #eaeaea;border-right:1px solid #eaeaea;z-index:1000;box-shadow:0 35px 35px -8px rgba(0,0,0,.1);-webkit-box-shadow:0 35px 35px -8px rgba(0,0,0,.1)}.wcpdf_document_settings_sections ul.active{display:block}.wcpdf_document_settings_sections ul li{box-sizing:border-box;padding:0;margin-bottom:0;border-bottom:1px solid #eaeaea;font-size:1.1em}.wcpdf_document_settings_sections ul li:hover{cursor:pointer;background:#51266b;color:#fff}.wcpdf_document_settings_sections ul li:hover a{color:#fff}.wcpdf_document_settings_sections ul li a{color:#000;text-decoration:none;padding:1.2em 1.6em;display:block}.wcpdf_document_settings_sections .arrow-down{font-size:.7em;color:#999;margin-left:8px;font-weight:400;float:right}.wcpdf_document_settings_sections p:hover,.wcpdf_document_settings_sections p:hover>.arrow-down{color:#222}.edit-next-number{opacity:.5}.edit-next-number:hover{opacity:1;cursor:pointer}body.woocommerce_page_wpo_wcpdf_options_page{background:#fff}.wrap [class$=icon32]+h2{font-size:18px;padding:1em}.wrap .notice{margin:15px 0 0}.nav-tab-wrapper a.nav-tab{background:0 0;border:none;border-bottom:3px solid transparent;padding:1em 0;margin:0 1.2em;font-size:15px}.nav-tab-wrapper a.nav-tab.nav-tab-active{border-bottom:3px solid #51266b}#wpo-wcpdf-preview-wrapper{width:100%;height:auto;position:relative;display:flex;align-items:flex-start}#wpo-wcpdf-preview-wrapper .preview-document,#wpo-wcpdf-preview-wrapper .sidebar{transition:.3s ease-in-out}#wpo-wcpdf-preview-wrapper .sidebar{height:auto;padding:4em 0 0 0;box-sizing:border-box;background:0 0;flex:0 0 35%;overflow-x:hidden}#wpo-wcpdf-preview-wrapper .sidebar>form{background:#fff;overflow:visible;padding:0;margin-left:2em;box-sizing:border-box;width:calc(100% - 4em);max-width:50vw}#wpo-wcpdf-preview-wrapper .sidebar>form.editor{max-width:none}#wpo-wcpdf-preview-wrapper .sidebar .form-table,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>th{display:block;width:100%;padding:0}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>th{padding-bottom:.6em}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td{padding-bottom:2.4em}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td p.description,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td>p.description{font-size:.85em;padding-top:.7em}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td>input[type=text],#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td>select,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td>textarea,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td>input[type=text],#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td>select,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td>textarea{max-width:none;width:100%}#wpo-wcpdf-preview-wrapper input[type=text][size]{width:auto!important;max-width:100%!important}#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td>input#next_invoice_number{width:auto!important}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table{display:table}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table tbody,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table tbody,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table tbody{display:table-row-group}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table tbody tr,#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table tr,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table tbody tr,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table tr,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table tbody tr,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table tr{display:table-row}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table>tbody>tr>th{display:table-cell;padding:15px 10px 15px 0;width:auto}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar .form-table>tbody>tr>th{width:300px!important}#wpo-wcpdf-settings .form-table .ui-tabs-nav{padding-left:0!important;margin-left:0!important}#wpo-wcpdf-settings .translations input,#wpo-wcpdf-settings .translations textarea{width:100%}#wpo-wcpdf-settings .wcpdf-attachment-settings-hint{border-left:4px solid #51266b}#wpo-wcpdf-settings .notice-info.inline{border-left-color:#51266b}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .sidebar{flex:0 0 100%}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .sidebar{flex:0 0 95%}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=full] .sidebar{flex:0 0 95%;margin-left:-95%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .sidebar{flex:0 0 95%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=sidebar] .sidebar{flex:0 0 35%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=full] .sidebar{margin-left:-35%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-from-preview-state=full] .sidebar{transition-delay:.4s}#wpo-wcpdf-preview-wrapper .preview-document{padding:0;box-sizing:border-box;position:sticky;top:2.4em;flex:0 0 60%}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .preview-document{display:none}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .preview-document{display:none}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=full] .preview-document{flex:0 0 95%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .preview-document{flex:0 0 60%;margin-right:-60%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=sidebar] .preview-document{flex:0 0 60%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=full] .preview-document{flex:0 0 95%}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-from-preview-state=full] .preview-document{transition-delay:.4s}.preview-document .preview{width:100%;box-sizing:border-box;padding-right:5%}.preview-document .preview>#preview-canvas{display:block;max-width:800px;max-height:85vh;width:auto!important;margin:0 auto;background:#fff;box-shadow:0 0 35px -8px rgba(0,0,0,.12);-webkit-box-shadow:0 0 35px -8px rgba(0,0,0,.12)}#wpo-wcpdf-preview-wrapper[data-preview-states="2"] #preview-canvas{max-height:170vh}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-from-preview-state=sidebar] #preview-canvas{max-height:170vh;transition:max-height .4s ease-in-out .3s}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-from-preview-state=full] #preview-canvas{transition:max-height .4s ease-in-out 0s}.preview-document .preview-data-wrapper{width:100%;height:4em}.preview-document .preview-data-wrapper .preview-document-type,.preview-document .preview-data-wrapper .preview-order-data{float:right}.preview-document .preview-data-wrapper .preview-document-type{margin-right:30px}.preview-document .preview-data-wrapper .preview-document-type ul>li{text-decoration:none;color:initial;padding:1.4em 1.6em}.preview-document .preview-data-wrapper .preview-document-type ul>li:hover{color:#fff!important}.preview-document .preview-data-wrapper .save-settings{padding:1em 0 0 0;float:right;overflow:hidden;position:relative}.preview-document .preview-data-wrapper .save-settings p{padding:0;margin:0 0 0 2em;position:relative;margin-right:-200px;transition:margin-right .3s ease-out}.preview-document .preview-data-wrapper .save-settings p:after{content:'';display:block;pointer-events:none;position:absolute;box-sizing:border-box;border-radius:3px;right:0;top:0;background:0 0;width:100%;height:100%;z-index:10;border:0 solid #fff;animation:border-pulse 4s infinite}@keyframes border-pulse{0%{border-color:rgba(255,255,255,0);border-width:8px}50%{border-color:#fff;border-width:0}}.preview-document .preview-data-wrapper .save-settings p input:focus{outline-width:0;box-shadow:none}.preview-document .preview-data p{padding:1.4em 0;margin:0;color:#666;text-align:right;cursor:pointer;font-weight:lighter;float:right}.preview-document .preview-data p.order-search{display:none}.preview-document .preview-data input{float:right;margin:1em 0 0 1em;padding:.1em .5em;width:20ch;margin-right:-25ch;display:none}.preview-document .preview-data input.active{margin-right:0;display:inline-block}.preview-document .preview-data ul{position:absolute;right:0;top:4em;background:#fff;box-shadow:0 0 25px -10px rgba(0,0,0,.2);-webkit-box-shadow:0 0 25px -10px rgba(0,0,0,.2);list-style:none;margin:0;padding:0;min-width:24em;display:block;height:0;overflow:hidden}.preview-document .preview-data ul.active{height:auto;z-index:1}.preview-document .preview-data ul li{box-sizing:border-box;padding:0;margin-bottom:0;border-bottom:1px solid #eaeaea;font-size:1.1em}.preview-document .preview-data ul li:hover{cursor:pointer;background:#51266b;color:#fff}.preview-document .preview-data ul li a,.preview-document .preview-data.preview-order-data ul li{display:block;padding:1.4em 1.6em}.preview-document .preview-data .arrow-down{font-size:.8em;color:#999;margin-left:8px}.preview-document .preview-data p:hover,.preview-document .preview-data p:hover>.arrow-down{color:#222}.preview-document .preview-data #preview-order-search-results{display:none;position:absolute;right:0;top:4em;width:300px;box-shadow:0 0 25px -10px rgba(0,0,0,.2);-webkit-box-shadow:0 0 25px -10px rgba(0,0,0,.2);padding:20px 0;background-color:#fff}.preview-document .preview-data #preview-order-search-results a{display:block;border-left:1px solid #999;border-right:1px solid #999;border-top:1px solid #999;color:#000;padding:10px;margin:0 20px;text-decoration:none;cursor:pointer}.preview-document .preview-data #preview-order-search-results a:last-child{border-bottom:1px solid #999}.preview-document .preview-data #preview-order-search-results a:hover{background-color:#51266b;color:#fff}.preview-document .preview-data #preview-order-search-results .order-number{font-weight:700}.preview-document .preview-data #preview-order-search-results .date,.preview-document .preview-data #preview-order-search-results .total{margin-top:6px;display:inline-block}.preview-document .preview-data #preview-order-search-results .total{float:right}.preview-document .preview-data #preview-order-search-results .error{margin:0 20px}.preview-document .preview-order-search-wrapper{position:relative;float:right}.preview-document .preview-order-search-wrapper img.preview-order-search-clear{position:absolute;width:30px;height:16px;top:22px;right:6px;display:none;cursor:pointer}#wpo-wcpdf-preview-wrapper .gutter{flex:0 0 5%;position:sticky;top:2.4em;height:170vh}#wpo-wcpdf-preview-wrapper[data-preview-states="2"][data-preview-state=closed] .gutter .slide-left,#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=closed] .gutter .slide-left{float:right}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=sidebar] .gutter .slide-left{border:none}#wpo-wcpdf-preview-wrapper .slider{box-sizing:border-box;padding-top:2.4em;color:#999;font-weight:700;cursor:pointer;font-size:.7em;line-height:1em;width:50%;height:100%;float:left}#wpo-wcpdf-preview-wrapper .slider.slide-left{text-align:right;padding-right:10px;border-right:1px solid #ccc}#wpo-wcpdf-preview-wrapper .slider.slide-right{text-align:left;padding-left:10px;border-left:1px solid #ccc;display:none}#wpo-wcpdf-preview-wrapper .gutter-arrow{width:0;height:0;border-top:3px solid transparent;border-bottom:3px solid transparent;display:block}#wpo-wcpdf-preview-wrapper .arrow-left{border-right:7px solid #999;float:right}#wpo-wcpdf-preview-wrapper .arrow-right{border-left:7px solid #999}#wpo-wcpdf-preview-wrapper .slider:hover>.arrow-left{border-right:7px solid #222}#wpo-wcpdf-preview-wrapper .slider:hover>.arrow-right{border-left:7px solid #222}#wpo-wcpdf-preview-wrapper .slider.slide-left:after{content:'Preview';position:absolute;top:1.55em;right:2em;font-size:1.4em;display:none}#wpo-wcpdf-preview-wrapper .slider.slide-right:after{content:'Settings';position:absolute;top:1.55em;left:2em;font-size:1.4em;display:none}#wpo-wcpdf-preview-wrapper[data-preview-states="1"] .gutter{display:none}#wpo-wcpdf-preview-wrapper[data-preview-states="3"][data-preview-state=sidebar] .gutter{height:100vh}#wpo-wcpdf-preview-wrapper[data-preview-state=full] .slide-right:after{display:inline-block}#wpo-wcpdf-preview-wrapper[data-preview-state=closed] .slide-left:after{display:inline-block}#wpo-wcpdf-preview-wrapper.static .gutter,#wpo-wcpdf-preview-wrapper.static .preview-document{position:static!important}#wpo-wcpdf-preview-wrapper.static .sidebar{height:170vh!important;overflow:hidden}#wpo-wcpdf-preview-wrapper input.readonly,#wpo-wcpdf-preview-wrapper input[readonly],#wpo-wcpdf-preview-wrapper textarea.readonly,#wpo-wcpdf-preview-wrapper textarea[readonly]{background-color:#f8f8f8}#wpo-wcpdf-preview-wrapper[data-preview-state=sidebar] .select2.select2-container{width:100%!important}@media screen and (min-width:1920px){.preview-document .preview>#preview-canvas{max-width:900px}}@media screen and (max-width:1200px){.preview-document .preview>#preview-canvas{max-width:680px}.nav-tab-wrapper a.nav-tab{padding:1em 2em;margin:0 .5em .5em 0;border:1px solid #ccc;box-sizing:border-box;height:4em}.nav-tab-wrapper a.nav-tab.nav-tab-active{border:3px solid #51266b}}@media screen and (max-width:960px){.preview-document .preview>#preview-canvas{width:80vw!important}#wpo-wcpdf-preview-wrapper .sidebar>form{max-width:100%}#wpo-wcpdf-preview-wrapper[data-preview-state=closed] .select2.select2-container{width:100%!important}#wpo-wcpdf-preview-wrapper .sidebar .form-table,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tbody>tr>th,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>td,#wpo-wcpdf-preview-wrapper .sidebar .form-table>tr>th{display:block!important}.preview-document .preview-data-wrapper{height:6em}.preview-document .preview-data p{padding:2.2em 0}#wpo-wcpdf-preview-wrapper .slider.slide-left:after,#wpo-wcpdf-preview-wrapper .slider.slide-right:after{top:1.5em;padding:1em;background:#fff;border:1px solid #ccc}#wpo-wcpdf-preview-wrapper .slider.slide-right:after{left:0}#wpo-wcpdf-preview-wrapper .slider.slide-left:after{right:0}}
|
composer.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
{
|
2 |
"require": {
|
3 |
-
"dompdf/dompdf": "^
|
4 |
"symfony/polyfill-mbstring": "1.20",
|
5 |
"symfony/polyfill-iconv": "1.20.0"
|
6 |
},
|
1 |
{
|
2 |
"require": {
|
3 |
+
"dompdf/dompdf": "^2.0",
|
4 |
"symfony/polyfill-mbstring": "1.20",
|
5 |
"symfony/polyfill-iconv": "1.20.0"
|
6 |
},
|
composer.lock
CHANGED
@@ -4,25 +4,26 @@
|
|
4 |
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
5 |
"This file is @generated automatically"
|
6 |
],
|
7 |
-
"content-hash": "
|
8 |
"packages": [
|
9 |
{
|
10 |
"name": "dompdf/dompdf",
|
11 |
-
"version": "
|
12 |
"source": {
|
13 |
"type": "git",
|
14 |
"url": "https://github.com/dompdf/dompdf.git",
|
15 |
-
"reference": "
|
16 |
},
|
17 |
"dist": {
|
18 |
"type": "zip",
|
19 |
-
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/
|
20 |
-
"reference": "
|
21 |
"shasum": ""
|
22 |
},
|
23 |
"require": {
|
24 |
"ext-dom": "*",
|
25 |
"ext-mbstring": "*",
|
|
|
26 |
"phenx/php-font-lib": "^0.5.4",
|
27 |
"phenx/php-svg-lib": "^0.3.3 || ^0.4.0",
|
28 |
"php": "^7.1 || ^8.0"
|
@@ -71,9 +72,78 @@
|
|
71 |
"homepage": "https://github.com/dompdf/dompdf",
|
72 |
"support": {
|
73 |
"issues": "https://github.com/dompdf/dompdf/issues",
|
74 |
-
"source": "https://github.com/dompdf/dompdf/tree/
|
75 |
},
|
76 |
-
"time": "2022-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
},
|
78 |
{
|
79 |
"name": "phenx/php-font-lib",
|
4 |
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
5 |
"This file is @generated automatically"
|
6 |
],
|
7 |
+
"content-hash": "83022a180b511a4284de8fcf7b79a57a",
|
8 |
"packages": [
|
9 |
{
|
10 |
"name": "dompdf/dompdf",
|
11 |
+
"version": "v2.0.0",
|
12 |
"source": {
|
13 |
"type": "git",
|
14 |
"url": "https://github.com/dompdf/dompdf.git",
|
15 |
+
"reference": "79573d8b8a141ec8a17312515de8740eed014fa9"
|
16 |
},
|
17 |
"dist": {
|
18 |
"type": "zip",
|
19 |
+
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/79573d8b8a141ec8a17312515de8740eed014fa9",
|
20 |
+
"reference": "79573d8b8a141ec8a17312515de8740eed014fa9",
|
21 |
"shasum": ""
|
22 |
},
|
23 |
"require": {
|
24 |
"ext-dom": "*",
|
25 |
"ext-mbstring": "*",
|
26 |
+
"masterminds/html5": "^2.0",
|
27 |
"phenx/php-font-lib": "^0.5.4",
|
28 |
"phenx/php-svg-lib": "^0.3.3 || ^0.4.0",
|
29 |
"php": "^7.1 || ^8.0"
|
72 |
"homepage": "https://github.com/dompdf/dompdf",
|
73 |
"support": {
|
74 |
"issues": "https://github.com/dompdf/dompdf/issues",
|
75 |
+
"source": "https://github.com/dompdf/dompdf/tree/v2.0.0"
|
76 |
},
|
77 |
+
"time": "2022-06-21T21:14:57+00:00"
|
78 |
+
},
|
79 |
+
{
|
80 |
+
"name": "masterminds/html5",
|
81 |
+
"version": "2.7.5",
|
82 |
+
"source": {
|
83 |
+
"type": "git",
|
84 |
+
"url": "https://github.com/Masterminds/html5-php.git",
|
85 |
+
"reference": "f640ac1bdddff06ea333a920c95bbad8872429ab"
|
86 |
+
},
|
87 |
+
"dist": {
|
88 |
+
"type": "zip",
|
89 |
+
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f640ac1bdddff06ea333a920c95bbad8872429ab",
|
90 |
+
"reference": "f640ac1bdddff06ea333a920c95bbad8872429ab",
|
91 |
+
"shasum": ""
|
92 |
+
},
|
93 |
+
"require": {
|
94 |
+
"ext-ctype": "*",
|
95 |
+
"ext-dom": "*",
|
96 |
+
"ext-libxml": "*",
|
97 |
+
"php": ">=5.3.0"
|
98 |
+
},
|
99 |
+
"require-dev": {
|
100 |
+
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7"
|
101 |
+
},
|
102 |
+
"type": "library",
|
103 |
+
"extra": {
|
104 |
+
"branch-alias": {
|
105 |
+
"dev-master": "2.7-dev"
|
106 |
+
}
|
107 |
+
},
|
108 |
+
"autoload": {
|
109 |
+
"psr-4": {
|
110 |
+
"Masterminds\\": "src"
|
111 |
+
}
|
112 |
+
},
|
113 |
+
"notification-url": "https://packagist.org/downloads/",
|
114 |
+
"license": [
|
115 |
+
"MIT"
|
116 |
+
],
|
117 |
+
"authors": [
|
118 |
+
{
|
119 |
+
"name": "Matt Butcher",
|
120 |
+
"email": "technosophos@gmail.com"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"name": "Matt Farina",
|
124 |
+
"email": "matt@mattfarina.com"
|
125 |
+
},
|
126 |
+
{
|
127 |
+
"name": "Asmir Mustafic",
|
128 |
+
"email": "goetas@gmail.com"
|
129 |
+
}
|
130 |
+
],
|
131 |
+
"description": "An HTML5 parser and serializer.",
|
132 |
+
"homepage": "http://masterminds.github.io/html5-php",
|
133 |
+
"keywords": [
|
134 |
+
"HTML5",
|
135 |
+
"dom",
|
136 |
+
"html",
|
137 |
+
"parser",
|
138 |
+
"querypath",
|
139 |
+
"serializer",
|
140 |
+
"xml"
|
141 |
+
],
|
142 |
+
"support": {
|
143 |
+
"issues": "https://github.com/Masterminds/html5-php/issues",
|
144 |
+
"source": "https://github.com/Masterminds/html5-php/tree/2.7.5"
|
145 |
+
},
|
146 |
+
"time": "2021-07-01T14:25:37+00:00"
|
147 |
},
|
148 |
{
|
149 |
"name": "phenx/php-font-lib",
|
includes/class-wcpdf-admin.php
CHANGED
@@ -18,7 +18,7 @@ class Admin {
|
|
18 |
add_action( 'manage_shop_order_posts_custom_column', array( $this, 'invoice_number_column_data' ), 2 );
|
19 |
add_action( 'add_meta_boxes_shop_order', array( $this, 'add_meta_boxes' ) );
|
20 |
if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '3.3', '>=' ) ) {
|
21 |
-
|
22 |
} else {
|
23 |
add_action( 'admin_footer', array( $this, 'bulk_actions_js' ) );
|
24 |
}
|
18 |
add_action( 'manage_shop_order_posts_custom_column', array( $this, 'invoice_number_column_data' ), 2 );
|
19 |
add_action( 'add_meta_boxes_shop_order', array( $this, 'add_meta_boxes' ) );
|
20 |
if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '3.3', '>=' ) ) {
|
21 |
+
add_filter( 'bulk_actions-edit-shop_order', array( $this, 'bulk_actions' ), 20 );
|
22 |
} else {
|
23 |
add_action( 'admin_footer', array( $this, 'bulk_actions_js' ) );
|
24 |
}
|
includes/class-wcpdf-font-synchronizer.php
CHANGED
@@ -17,7 +17,7 @@ class Font_Synchronizer {
|
|
17 |
*
|
18 |
* @var string
|
19 |
*/
|
20 |
-
public $font_cache_filename = "
|
21 |
|
22 |
/**
|
23 |
* Vanilla instance of dompdf
|
@@ -48,7 +48,7 @@ class Font_Synchronizer {
|
|
48 |
$plugin_fonts = $this->get_plugin_fonts();
|
49 |
$dompdf_fonts = $this->get_dompdf_fonts();
|
50 |
if ( $merge_with_local ) {
|
51 |
-
$local_fonts
|
52 |
} else {
|
53 |
$local_fonts = array();
|
54 |
}
|
@@ -72,11 +72,8 @@ class Font_Synchronizer {
|
|
72 |
|
73 |
// normalize one last time
|
74 |
$local_fonts = $this->normalize_font_paths( $local_fonts );
|
75 |
-
|
76 |
// rebuild font cache file
|
77 |
-
$
|
78 |
-
$fonts_export = str_replace( '\'' . untrailingslashit( $destination ) , '$fontDir . \'', $fonts_export );
|
79 |
-
$cacheData = sprintf( "<?php return %s;%s?>", $fonts_export, PHP_EOL );
|
80 |
// write file with merged cache data
|
81 |
file_put_contents( $destination . $this->font_cache_filename, $cacheData );
|
82 |
}
|
@@ -136,11 +133,17 @@ class Font_Synchronizer {
|
|
136 |
*/
|
137 |
public function get_local_fonts( $path ) {
|
138 |
// prepare variables used in the cache list
|
139 |
-
$fontDir
|
140 |
-
$rootDir
|
141 |
-
$cache_file
|
|
|
|
|
142 |
if ( is_readable( $cache_file ) ) {
|
143 |
-
$
|
|
|
|
|
|
|
|
|
144 |
} else {
|
145 |
$font_data = array();
|
146 |
}
|
17 |
*
|
18 |
* @var string
|
19 |
*/
|
20 |
+
public $font_cache_filename = "installed-fonts.json";
|
21 |
|
22 |
/**
|
23 |
* Vanilla instance of dompdf
|
48 |
$plugin_fonts = $this->get_plugin_fonts();
|
49 |
$dompdf_fonts = $this->get_dompdf_fonts();
|
50 |
if ( $merge_with_local ) {
|
51 |
+
$local_fonts = $this->get_local_fonts( $destination );
|
52 |
} else {
|
53 |
$local_fonts = array();
|
54 |
}
|
72 |
|
73 |
// normalize one last time
|
74 |
$local_fonts = $this->normalize_font_paths( $local_fonts );
|
|
|
75 |
// rebuild font cache file
|
76 |
+
$cacheData = json_encode( $local_fonts, JSON_PRETTY_PRINT );
|
|
|
|
|
77 |
// write file with merged cache data
|
78 |
file_put_contents( $destination . $this->font_cache_filename, $cacheData );
|
79 |
}
|
133 |
*/
|
134 |
public function get_local_fonts( $path ) {
|
135 |
// prepare variables used in the cache list
|
136 |
+
$fontDir = $path;
|
137 |
+
$rootDir = $this->dompdf->getOptions()->getRootDir();
|
138 |
+
$cache_file = trailingslashit( $path ) . $this->font_cache_filename;
|
139 |
+
$legacy_cache_file = trailingslashit( $path ) . 'dompdf_font_family_cache.php'; // Dompdf <2.0
|
140 |
+
|
141 |
if ( is_readable( $cache_file ) ) {
|
142 |
+
$json_data = file_get_contents( $cache_file );
|
143 |
+
$font_data = json_decode( $json_data, true );
|
144 |
+
} elseif ( is_readable( $legacy_cache_file ) ) {
|
145 |
+
$font_data = include $legacy_cache_file;
|
146 |
+
@unlink( $legacy_cache_file );
|
147 |
} else {
|
148 |
$font_data = array();
|
149 |
}
|
includes/class-wcpdf-frontend.php
CHANGED
@@ -139,6 +139,11 @@ class Frontend {
|
|
139 |
return;
|
140 |
}
|
141 |
|
|
|
|
|
|
|
|
|
|
|
142 |
// Link text
|
143 |
if( ! empty( $values['link_text'] ) ) {
|
144 |
$link_text = $values['link_text'];
|
139 |
return;
|
140 |
}
|
141 |
|
142 |
+
$invoice = wcpdf_get_invoice( $order );
|
143 |
+
if ( ! $invoice || ! $invoice->is_allowed() ) {
|
144 |
+
return;
|
145 |
+
}
|
146 |
+
|
147 |
// Link text
|
148 |
if( ! empty( $values['link_text'] ) ) {
|
149 |
$link_text = $values['link_text'];
|
includes/class-wcpdf-install.php
CHANGED
@@ -404,7 +404,16 @@ class Install {
|
|
404 |
}
|
405 |
}
|
406 |
}
|
407 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
408 |
}
|
409 |
|
410 |
/**
|
404 |
}
|
405 |
}
|
406 |
}
|
407 |
+
|
408 |
+
// 3.0.0-dev-1: remove saved option 'use_html5_parser'
|
409 |
+
if ( version_compare( $installed_version, '3.0.0-dev-1', '<' ) ) {
|
410 |
+
// removes 'HTML5 parser' setting value
|
411 |
+
$debug_settings = get_option( 'wpo_wcpdf_settings_debug', array() );
|
412 |
+
if ( ! empty( $debug_settings['use_html5_parser'] ) ) {
|
413 |
+
unset( $debug_settings['use_html5_parser'] );
|
414 |
+
update_option( 'wpo_wcpdf_settings_debug', $debug_settings );
|
415 |
+
}
|
416 |
+
}
|
417 |
}
|
418 |
|
419 |
/**
|
includes/class-wcpdf-pdf-maker.php
CHANGED
@@ -34,16 +34,14 @@ class PDF_Maker {
|
|
34 |
|
35 |
// set options
|
36 |
$options = new Options( apply_filters( 'wpo_wcpdf_dompdf_options', array(
|
37 |
-
'tempDir'
|
38 |
-
'fontDir'
|
39 |
-
'fontCache'
|
40 |
-
'chroot'
|
41 |
-
'logOutputFile'
|
42 |
-
'defaultFont'
|
43 |
-
'isRemoteEnabled'
|
44 |
-
|
45 |
-
'isHtml5ParserEnabled' => ( isset(WPO_WCPDF()->settings->debug_settings['use_html5_parser']) && extension_loaded('iconv') ) ? true : false,
|
46 |
-
'isFontSubsettingEnabled' => $this->settings['font_subsetting'],
|
47 |
) ) );
|
48 |
|
49 |
// instantiate and use the dompdf class
|
34 |
|
35 |
// set options
|
36 |
$options = new Options( apply_filters( 'wpo_wcpdf_dompdf_options', array(
|
37 |
+
'tempDir' => WPO_WCPDF()->main->get_tmp_path( 'dompdf' ),
|
38 |
+
'fontDir' => WPO_WCPDF()->main->get_tmp_path( 'fonts' ),
|
39 |
+
'fontCache' => WPO_WCPDF()->main->get_tmp_path( 'fonts' ),
|
40 |
+
'chroot' => $this->get_chroot_paths(),
|
41 |
+
'logOutputFile' => WPO_WCPDF()->main->get_tmp_path( 'dompdf' ) . "/log.htm",
|
42 |
+
'defaultFont' => 'dejavu sans',
|
43 |
+
'isRemoteEnabled' => true,
|
44 |
+
'isFontSubsettingEnabled' => $this->settings['font_subsetting'],
|
|
|
|
|
45 |
) ) );
|
46 |
|
47 |
// instantiate and use the dompdf class
|
includes/class-wcpdf-settings-callbacks.php
CHANGED
@@ -35,6 +35,18 @@ class Settings_Callbacks {
|
|
35 |
public function custom_fields_section() {
|
36 |
echo wp_kses_post( __( 'These are used for the (optional) footer columns in the <em>Modern (Premium)</em> template, but can also be used for other elements in your custom template' , 'woocommerce-pdf-invoices-packing-slips' ) );
|
37 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
/**
|
40 |
* Checkbox callback.
|
@@ -664,4 +676,4 @@ class Settings_Callbacks {
|
|
664 |
|
665 |
endif; // class_exists
|
666 |
|
667 |
-
return new Settings_Callbacks();
|
35 |
public function custom_fields_section() {
|
36 |
echo wp_kses_post( __( 'These are used for the (optional) footer columns in the <em>Modern (Premium)</em> template, but can also be used for other elements in your custom template' , 'woocommerce-pdf-invoices-packing-slips' ) );
|
37 |
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* HTML section callback.
|
41 |
+
*
|
42 |
+
* @return void.
|
43 |
+
*/
|
44 |
+
public function html_section( $args ) {
|
45 |
+
extract( $this->normalize_settings_args( $args ) );
|
46 |
+
|
47 |
+
// output HTML
|
48 |
+
echo wp_kses_post( $html );
|
49 |
+
}
|
50 |
|
51 |
/**
|
52 |
* Checkbox callback.
|
676 |
|
677 |
endif; // class_exists
|
678 |
|
679 |
+
return new Settings_Callbacks();
|
includes/class-wcpdf-settings-debug.php
CHANGED
@@ -258,17 +258,6 @@ class Settings_Debug {
|
|
258 |
__( 'You can also add <code>&output=html</code> to the URL to apply this on a per-order basis.', 'woocommerce-pdf-invoices-packing-slips' ),
|
259 |
)
|
260 |
),
|
261 |
-
array(
|
262 |
-
'type' => 'setting',
|
263 |
-
'id' => 'use_html5_parser',
|
264 |
-
'title' => __( 'Use alternative HTML5 parser to parse HTML', 'woocommerce-pdf-invoices-packing-slips' ),
|
265 |
-
'callback' => 'checkbox',
|
266 |
-
'section' => 'debug_settings',
|
267 |
-
'args' => array(
|
268 |
-
'option_name' => $option_name,
|
269 |
-
'id' => 'use_html5_parser',
|
270 |
-
)
|
271 |
-
),
|
272 |
array(
|
273 |
'type' => 'setting',
|
274 |
'id' => 'log_to_order_notes',
|
258 |
__( 'You can also add <code>&output=html</code> to the URL to apply this on a per-order basis.', 'woocommerce-pdf-invoices-packing-slips' ),
|
259 |
)
|
260 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
261 |
array(
|
262 |
'type' => 'setting',
|
263 |
'id' => 'log_to_order_notes',
|
includes/class-wcpdf-settings-general.php
CHANGED
@@ -300,15 +300,14 @@ class Settings_General {
|
|
300 |
*/
|
301 |
public function find_templates() {
|
302 |
$installed_templates = array();
|
303 |
-
|
304 |
// get base paths
|
305 |
-
$template_base_path
|
306 |
-
$template_base_path
|
307 |
-
$template_paths
|
308 |
// note the order: child-theme before theme, so that array_unique filters out parent doubles
|
309 |
-
'default'
|
310 |
-
'child-theme'
|
311 |
-
'theme'
|
312 |
);
|
313 |
|
314 |
$template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
|
@@ -319,10 +318,10 @@ class Settings_General {
|
|
319 |
$forwardslash_basepath = str_replace( '\\', '/', WP_CONTENT_DIR );
|
320 |
}
|
321 |
|
322 |
-
foreach ($template_paths as $template_source => $template_path) {
|
323 |
$dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR );
|
324 |
|
325 |
-
foreach ($dirs as $dir) {
|
326 |
// we're stripping abspath to make the plugin settings more portable
|
327 |
$forwardslash_dir = str_replace( '\\', '/', $dir );
|
328 |
$installed_templates[ str_replace( $forwardslash_basepath, '', $forwardslash_dir ) ] = basename($dir);
|
@@ -332,7 +331,7 @@ class Settings_General {
|
|
332 |
// remove parent doubles
|
333 |
$installed_templates = array_unique($installed_templates);
|
334 |
|
335 |
-
if (empty($installed_templates)) {
|
336 |
// fallback to Simple template for servers with glob() disabled
|
337 |
$simple_template_path = str_replace( ABSPATH, '', $template_paths['default'] . 'Simple' );
|
338 |
$installed_templates[$simple_template_path] = 'Simple';
|
300 |
*/
|
301 |
public function find_templates() {
|
302 |
$installed_templates = array();
|
|
|
303 |
// get base paths
|
304 |
+
$template_base_path = ( function_exists( 'WC' ) && is_callable( array( WC(), 'template_path' ) ) ) ? WC()->template_path() : apply_filters( 'woocommerce_template_path', 'woocommerce/' );
|
305 |
+
$template_base_path = untrailingslashit( $template_base_path );
|
306 |
+
$template_paths = array (
|
307 |
// note the order: child-theme before theme, so that array_unique filters out parent doubles
|
308 |
+
'default' => WPO_WCPDF()->plugin_path() . '/templates/',
|
309 |
+
'child-theme' => get_stylesheet_directory() . "/{$template_base_path}/pdf/",
|
310 |
+
'theme' => get_template_directory() . "/{$template_base_path}/pdf/",
|
311 |
);
|
312 |
|
313 |
$template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
|
318 |
$forwardslash_basepath = str_replace( '\\', '/', WP_CONTENT_DIR );
|
319 |
}
|
320 |
|
321 |
+
foreach ( $template_paths as $template_source => $template_path ) {
|
322 |
$dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR );
|
323 |
|
324 |
+
foreach ( $dirs as $dir ) {
|
325 |
// we're stripping abspath to make the plugin settings more portable
|
326 |
$forwardslash_dir = str_replace( '\\', '/', $dir );
|
327 |
$installed_templates[ str_replace( $forwardslash_basepath, '', $forwardslash_dir ) ] = basename($dir);
|
331 |
// remove parent doubles
|
332 |
$installed_templates = array_unique($installed_templates);
|
333 |
|
334 |
+
if ( empty( $installed_templates ) ) {
|
335 |
// fallback to Simple template for servers with glob() disabled
|
336 |
$simple_template_path = str_replace( ABSPATH, '', $template_paths['default'] . 'Simple' );
|
337 |
$installed_templates[$simple_template_path] = 'Simple';
|
includes/class-wcpdf-settings.php
CHANGED
@@ -480,27 +480,26 @@ class Settings {
|
|
480 |
}
|
481 |
|
482 |
$installed_templates = array();
|
483 |
-
|
484 |
// get base paths
|
485 |
-
$template_base_path
|
486 |
-
$template_base_path
|
487 |
-
$template_paths
|
488 |
// note the order: theme before child-theme, so that child theme is always preferred (overwritten)
|
489 |
-
'default'
|
490 |
-
'theme'
|
491 |
-
'child-theme'
|
492 |
);
|
493 |
|
494 |
$template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
|
495 |
|
496 |
-
foreach ($template_paths as $template_source => $template_path) {
|
497 |
$dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR );
|
498 |
|
499 |
foreach ( $dirs as $dir ) {
|
500 |
-
$clean_dir
|
501 |
$template_name = basename( $clean_dir );
|
502 |
// let child theme override parent theme
|
503 |
-
$group = ( $template_source == 'child-theme' ) ? 'theme' : $template_source;
|
504 |
$installed_templates[ $clean_dir ] = "{$group}/{$template_name}" ;
|
505 |
}
|
506 |
}
|
@@ -674,6 +673,39 @@ class Settings {
|
|
674 |
return wp_send_json_success( $html );
|
675 |
}
|
676 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
677 |
}
|
678 |
|
679 |
endif; // class_exists
|
480 |
}
|
481 |
|
482 |
$installed_templates = array();
|
|
|
483 |
// get base paths
|
484 |
+
$template_base_path = ( function_exists( 'WC' ) && is_callable( array( WC(), 'template_path' ) ) ) ? WC()->template_path() : apply_filters( 'woocommerce_template_path', 'woocommerce/' );
|
485 |
+
$template_base_path = untrailingslashit( $template_base_path );
|
486 |
+
$template_paths = array (
|
487 |
// note the order: theme before child-theme, so that child theme is always preferred (overwritten)
|
488 |
+
'default' => WPO_WCPDF()->plugin_path() . '/templates/',
|
489 |
+
'theme' => get_template_directory() . "/{$template_base_path}/pdf/",
|
490 |
+
'child-theme' => get_stylesheet_directory() . "/{$template_base_path}/pdf/",
|
491 |
);
|
492 |
|
493 |
$template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
|
494 |
|
495 |
+
foreach ( $template_paths as $template_source => $template_path ) {
|
496 |
$dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR );
|
497 |
|
498 |
foreach ( $dirs as $dir ) {
|
499 |
+
$clean_dir = $this->normalize_path( $dir );
|
500 |
$template_name = basename( $clean_dir );
|
501 |
// let child theme override parent theme
|
502 |
+
$group = ( $template_source == 'child-theme' ) ? 'theme' : $template_source;
|
503 |
$installed_templates[ $clean_dir ] = "{$group}/{$template_name}" ;
|
504 |
}
|
505 |
}
|
673 |
return wp_send_json_success( $html );
|
674 |
}
|
675 |
|
676 |
+
public function move_setting_after_id( $settings, $insert_settings, $after_setting_id ) {
|
677 |
+
$pos = 1; // this is already +1 to insert after the actual pos
|
678 |
+
foreach ( $settings as $setting ) {
|
679 |
+
if ( isset( $setting['id'] ) && $setting['id'] == $after_setting_id ) {
|
680 |
+
$section = $setting['section'];
|
681 |
+
break;
|
682 |
+
} else {
|
683 |
+
$pos++;
|
684 |
+
}
|
685 |
+
}
|
686 |
+
|
687 |
+
// replace section
|
688 |
+
if ( isset( $section ) ) {
|
689 |
+
foreach ( $insert_settings as $key => $insert_setting ) {
|
690 |
+
$insert_settings[$key]['section'] = $section;
|
691 |
+
}
|
692 |
+
} else {
|
693 |
+
$empty_section = array(
|
694 |
+
array(
|
695 |
+
'type' => 'section',
|
696 |
+
'id' => 'custom',
|
697 |
+
'title' => '',
|
698 |
+
'callback' => 'section',
|
699 |
+
),
|
700 |
+
);
|
701 |
+
$insert_settings = array_merge( $empty_section, $insert_settings );
|
702 |
+
}
|
703 |
+
// insert our api settings
|
704 |
+
$new_settings = array_merge( array_slice( $settings, 0, $pos, true ), $insert_settings, array_slice( $settings, $pos, NULL, true ) );
|
705 |
+
|
706 |
+
return $new_settings;
|
707 |
+
}
|
708 |
+
|
709 |
}
|
710 |
|
711 |
endif; // class_exists
|
includes/documents/class-wcpdf-packing-slip.php
CHANGED
@@ -146,7 +146,32 @@ class Packing_Slip extends Order_Document_Methods {
|
|
146 |
)
|
147 |
),
|
148 |
);
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
// allow plugins to alter settings fields
|
152 |
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_documents_packing_slip', $settings_fields, $page, $option_group, $option_name );
|
@@ -175,4 +200,4 @@ class Packing_Slip extends Order_Document_Methods {
|
|
175 |
|
176 |
endif; // class_exists
|
177 |
|
178 |
-
return new Packing_Slip();
|
146 |
)
|
147 |
),
|
148 |
);
|
149 |
+
|
150 |
+
if ( ! function_exists( 'WPO_WCPDF_Pro' ) ) {
|
151 |
+
ob_start();
|
152 |
+
?>
|
153 |
+
<div class="notice notice-info inline">
|
154 |
+
<p><a href="https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/" target="_blank"><?php _e( 'Upgrade to our Professional extension to attach packing slips to any email!', 'woocommerce-pdf-invoices-packing-slips' ); ?></a></p>
|
155 |
+
</div>
|
156 |
+
<?php
|
157 |
+
$html = ob_get_clean();
|
158 |
+
|
159 |
+
$pro_notice = array(
|
160 |
+
array(
|
161 |
+
'type' => 'setting',
|
162 |
+
'id' => 'attach_to_email_ids',
|
163 |
+
'title' => __( 'Attach to:', 'woocommerce-pdf-invoices-packing-slips' ),
|
164 |
+
'callback' => 'html_section',
|
165 |
+
'section' => 'packing_slip',
|
166 |
+
'args' => array(
|
167 |
+
'option_name' => $option_name,
|
168 |
+
'id' => 'attach_to_email_ids',
|
169 |
+
'html' => $html,
|
170 |
+
)
|
171 |
+
),
|
172 |
+
);
|
173 |
+
$settings_fields = WPO_WCPDF()->settings->move_setting_after_id( $settings_fields, $pro_notice, 'enabled' );
|
174 |
+
}
|
175 |
|
176 |
// allow plugins to alter settings fields
|
177 |
$settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_documents_packing_slip', $settings_fields, $page, $option_group, $option_name );
|
200 |
|
201 |
endif; // class_exists
|
202 |
|
203 |
+
return new Packing_Slip();
|
readme.txt
CHANGED
@@ -5,7 +5,7 @@ Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice,
|
|
5 |
Requires at least: 3.5
|
6 |
Tested up to: 6.0
|
7 |
Requires PHP: 7.1
|
8 |
-
Stable tag:
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
@@ -102,6 +102,12 @@ There's a setting on the Status tab of the settings page that allows you to togg
|
|
102 |
|
103 |
== Changelog ==
|
104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
= 2.16.0 =
|
106 |
* Security: Fix authenticated reflected XSS on the settings page
|
107 |
* Fix: Redirection URLs in wizard and when sending emails manually
|
5 |
Requires at least: 3.5
|
6 |
Tested up to: 6.0
|
7 |
Requires PHP: 7.1
|
8 |
+
Stable tag: 3.0.0
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
102 |
|
103 |
== Changelog ==
|
104 |
|
105 |
+
= 3.0.0 =
|
106 |
+
* Libraries: Updated dompdf to 2.0.0, addressing security vulnerabilities and introducing some changes that could possibly break high level customized setups ([more information](https://github.com/dompdf/dompdf/releases/tag/v2.0.0))
|
107 |
+
* Fix: respect custom (filtered) woocommerce template paths
|
108 |
+
* Fix: Check if the invoice is allowed before the shortcode output
|
109 |
+
* UI: Link to Professional extension for packing slip attachments
|
110 |
+
|
111 |
= 2.16.0 =
|
112 |
* Security: Fix authenticated reflected XSS on the settings page
|
113 |
* Fix: Redirection URLs in wizard and when sending emails manually
|
vendor/composer/autoload_classmap.php
CHANGED
@@ -8,9 +8,4 @@ $baseDir = dirname($vendorDir);
|
|
8 |
return array(
|
9 |
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
10 |
'Dompdf\\Cpdf' => $vendorDir . '/dompdf/dompdf/lib/Cpdf.php',
|
11 |
-
'HTML5_Data' => $vendorDir . '/dompdf/dompdf/lib/html5lib/Data.php',
|
12 |
-
'HTML5_InputStream' => $vendorDir . '/dompdf/dompdf/lib/html5lib/InputStream.php',
|
13 |
-
'HTML5_Parser' => $vendorDir . '/dompdf/dompdf/lib/html5lib/Parser.php',
|
14 |
-
'HTML5_Tokenizer' => $vendorDir . '/dompdf/dompdf/lib/html5lib/Tokenizer.php',
|
15 |
-
'HTML5_TreeBuilder' => $vendorDir . '/dompdf/dompdf/lib/html5lib/TreeBuilder.php',
|
16 |
);
|
8 |
return array(
|
9 |
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
10 |
'Dompdf\\Cpdf' => $vendorDir . '/dompdf/dompdf/lib/Cpdf.php',
|
|
|
|
|
|
|
|
|
|
|
11 |
);
|
vendor/composer/autoload_psr4.php
CHANGED
@@ -10,6 +10,7 @@ return array(
|
|
10 |
'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
|
11 |
'Svg\\' => array($vendorDir . '/phenx/php-svg-lib/src/Svg'),
|
12 |
'Sabberworm\\CSS\\' => array($vendorDir . '/sabberworm/php-css-parser/src'),
|
|
|
13 |
'FontLib\\' => array($vendorDir . '/phenx/php-font-lib/src/FontLib'),
|
14 |
'Dompdf\\' => array($vendorDir . '/dompdf/dompdf/src'),
|
15 |
);
|
10 |
'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
|
11 |
'Svg\\' => array($vendorDir . '/phenx/php-svg-lib/src/Svg'),
|
12 |
'Sabberworm\\CSS\\' => array($vendorDir . '/sabberworm/php-css-parser/src'),
|
13 |
+
'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
|
14 |
'FontLib\\' => array($vendorDir . '/phenx/php-font-lib/src/FontLib'),
|
15 |
'Dompdf\\' => array($vendorDir . '/dompdf/dompdf/src'),
|
16 |
);
|
vendor/composer/autoload_static.php
CHANGED
@@ -19,6 +19,10 @@ class ComposerStaticInita44f2cb79329ce99e54227132018137a
|
|
19 |
'Svg\\' => 4,
|
20 |
'Sabberworm\\CSS\\' => 15,
|
21 |
),
|
|
|
|
|
|
|
|
|
22 |
'F' =>
|
23 |
array (
|
24 |
'FontLib\\' => 8,
|
@@ -46,6 +50,10 @@ class ComposerStaticInita44f2cb79329ce99e54227132018137a
|
|
46 |
array (
|
47 |
0 => __DIR__ . '/..' . '/sabberworm/php-css-parser/src',
|
48 |
),
|
|
|
|
|
|
|
|
|
49 |
'FontLib\\' =>
|
50 |
array (
|
51 |
0 => __DIR__ . '/..' . '/phenx/php-font-lib/src/FontLib',
|
@@ -59,11 +67,6 @@ class ComposerStaticInita44f2cb79329ce99e54227132018137a
|
|
59 |
public static $classMap = array (
|
60 |
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
61 |
'Dompdf\\Cpdf' => __DIR__ . '/..' . '/dompdf/dompdf/lib/Cpdf.php',
|
62 |
-
'HTML5_Data' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/Data.php',
|
63 |
-
'HTML5_InputStream' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/InputStream.php',
|
64 |
-
'HTML5_Parser' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/Parser.php',
|
65 |
-
'HTML5_Tokenizer' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/Tokenizer.php',
|
66 |
-
'HTML5_TreeBuilder' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/TreeBuilder.php',
|
67 |
);
|
68 |
|
69 |
public static function getInitializer(ClassLoader $loader)
|
19 |
'Svg\\' => 4,
|
20 |
'Sabberworm\\CSS\\' => 15,
|
21 |
),
|
22 |
+
'M' =>
|
23 |
+
array (
|
24 |
+
'Masterminds\\' => 12,
|
25 |
+
),
|
26 |
'F' =>
|
27 |
array (
|
28 |
'FontLib\\' => 8,
|
50 |
array (
|
51 |
0 => __DIR__ . '/..' . '/sabberworm/php-css-parser/src',
|
52 |
),
|
53 |
+
'Masterminds\\' =>
|
54 |
+
array (
|
55 |
+
0 => __DIR__ . '/..' . '/masterminds/html5/src',
|
56 |
+
),
|
57 |
'FontLib\\' =>
|
58 |
array (
|
59 |
0 => __DIR__ . '/..' . '/phenx/php-font-lib/src/FontLib',
|
67 |
public static $classMap = array (
|
68 |
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
69 |
'Dompdf\\Cpdf' => __DIR__ . '/..' . '/dompdf/dompdf/lib/Cpdf.php',
|
|
|
|
|
|
|
|
|
|
|
70 |
);
|
71 |
|
72 |
public static function getInitializer(ClassLoader $loader)
|
vendor/composer/installed.json
CHANGED
@@ -2,22 +2,23 @@
|
|
2 |
"packages": [
|
3 |
{
|
4 |
"name": "dompdf/dompdf",
|
5 |
-
"version": "
|
6 |
-
"version_normalized": "
|
7 |
"source": {
|
8 |
"type": "git",
|
9 |
"url": "https://github.com/dompdf/dompdf.git",
|
10 |
-
"reference": "
|
11 |
},
|
12 |
"dist": {
|
13 |
"type": "zip",
|
14 |
-
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/
|
15 |
-
"reference": "
|
16 |
"shasum": ""
|
17 |
},
|
18 |
"require": {
|
19 |
"ext-dom": "*",
|
20 |
"ext-mbstring": "*",
|
|
|
21 |
"phenx/php-font-lib": "^0.5.4",
|
22 |
"phenx/php-svg-lib": "^0.3.3 || ^0.4.0",
|
23 |
"php": "^7.1 || ^8.0"
|
@@ -35,7 +36,7 @@
|
|
35 |
"ext-imagick": "Improves image processing performance",
|
36 |
"ext-zlib": "Needed for pdf stream compression"
|
37 |
},
|
38 |
-
"time": "2022-
|
39 |
"type": "library",
|
40 |
"installation-source": "dist",
|
41 |
"autoload": {
|
@@ -68,10 +69,82 @@
|
|
68 |
"homepage": "https://github.com/dompdf/dompdf",
|
69 |
"support": {
|
70 |
"issues": "https://github.com/dompdf/dompdf/issues",
|
71 |
-
"source": "https://github.com/dompdf/dompdf/tree/
|
72 |
},
|
73 |
"install-path": "../dompdf/dompdf"
|
74 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
{
|
76 |
"name": "phenx/php-font-lib",
|
77 |
"version": "0.5.4",
|
@@ -258,12 +331,12 @@
|
|
258 |
},
|
259 |
"installation-source": "dist",
|
260 |
"autoload": {
|
261 |
-
"psr-4": {
|
262 |
-
"Symfony\\Polyfill\\Iconv\\": ""
|
263 |
-
},
|
264 |
"files": [
|
265 |
"bootstrap.php"
|
266 |
-
]
|
|
|
|
|
|
|
267 |
},
|
268 |
"notification-url": "https://packagist.org/downloads/",
|
269 |
"license": [
|
@@ -391,6 +464,6 @@
|
|
391 |
"install-path": "../symfony/polyfill-mbstring"
|
392 |
}
|
393 |
],
|
394 |
-
"dev":
|
395 |
"dev-package-names": []
|
396 |
}
|
2 |
"packages": [
|
3 |
{
|
4 |
"name": "dompdf/dompdf",
|
5 |
+
"version": "v2.0.0",
|
6 |
+
"version_normalized": "2.0.0.0",
|
7 |
"source": {
|
8 |
"type": "git",
|
9 |
"url": "https://github.com/dompdf/dompdf.git",
|
10 |
+
"reference": "79573d8b8a141ec8a17312515de8740eed014fa9"
|
11 |
},
|
12 |
"dist": {
|
13 |
"type": "zip",
|
14 |
+
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/79573d8b8a141ec8a17312515de8740eed014fa9",
|
15 |
+
"reference": "79573d8b8a141ec8a17312515de8740eed014fa9",
|
16 |
"shasum": ""
|
17 |
},
|
18 |
"require": {
|
19 |
"ext-dom": "*",
|
20 |
"ext-mbstring": "*",
|
21 |
+
"masterminds/html5": "^2.0",
|
22 |
"phenx/php-font-lib": "^0.5.4",
|
23 |
"phenx/php-svg-lib": "^0.3.3 || ^0.4.0",
|
24 |
"php": "^7.1 || ^8.0"
|
36 |
"ext-imagick": "Improves image processing performance",
|
37 |
"ext-zlib": "Needed for pdf stream compression"
|
38 |
},
|
39 |
+
"time": "2022-06-21T21:14:57+00:00",
|
40 |
"type": "library",
|
41 |
"installation-source": "dist",
|
42 |
"autoload": {
|
69 |
"homepage": "https://github.com/dompdf/dompdf",
|
70 |
"support": {
|
71 |
"issues": "https://github.com/dompdf/dompdf/issues",
|
72 |
+
"source": "https://github.com/dompdf/dompdf/tree/v2.0.0"
|
73 |
},
|
74 |
"install-path": "../dompdf/dompdf"
|
75 |
},
|
76 |
+
{
|
77 |
+
"name": "masterminds/html5",
|
78 |
+
"version": "2.7.5",
|
79 |
+
"version_normalized": "2.7.5.0",
|
80 |
+
"source": {
|
81 |
+
"type": "git",
|
82 |
+
"url": "https://github.com/Masterminds/html5-php.git",
|
83 |
+
"reference": "f640ac1bdddff06ea333a920c95bbad8872429ab"
|
84 |
+
},
|
85 |
+
"dist": {
|
86 |
+
"type": "zip",
|
87 |
+
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f640ac1bdddff06ea333a920c95bbad8872429ab",
|
88 |
+
"reference": "f640ac1bdddff06ea333a920c95bbad8872429ab",
|
89 |
+
"shasum": ""
|
90 |
+
},
|
91 |
+
"require": {
|
92 |
+
"ext-ctype": "*",
|
93 |
+
"ext-dom": "*",
|
94 |
+
"ext-libxml": "*",
|
95 |
+
"php": ">=5.3.0"
|
96 |
+
},
|
97 |
+
"require-dev": {
|
98 |
+
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7"
|
99 |
+
},
|
100 |
+
"time": "2021-07-01T14:25:37+00:00",
|
101 |
+
"type": "library",
|
102 |
+
"extra": {
|
103 |
+
"branch-alias": {
|
104 |
+
"dev-master": "2.7-dev"
|
105 |
+
}
|
106 |
+
},
|
107 |
+
"installation-source": "dist",
|
108 |
+
"autoload": {
|
109 |
+
"psr-4": {
|
110 |
+
"Masterminds\\": "src"
|
111 |
+
}
|
112 |
+
},
|
113 |
+
"notification-url": "https://packagist.org/downloads/",
|
114 |
+
"license": [
|
115 |
+
"MIT"
|
116 |
+
],
|
117 |
+
"authors": [
|
118 |
+
{
|
119 |
+
"name": "Matt Butcher",
|
120 |
+
"email": "technosophos@gmail.com"
|
121 |
+
},
|
122 |
+
{
|
123 |
+
"name": "Matt Farina",
|
124 |
+
"email": "matt@mattfarina.com"
|
125 |
+
},
|
126 |
+
{
|
127 |
+
"name": "Asmir Mustafic",
|
128 |
+
"email": "goetas@gmail.com"
|
129 |
+
}
|
130 |
+
],
|
131 |
+
"description": "An HTML5 parser and serializer.",
|
132 |
+
"homepage": "http://masterminds.github.io/html5-php",
|
133 |
+
"keywords": [
|
134 |
+
"HTML5",
|
135 |
+
"dom",
|
136 |
+
"html",
|
137 |
+
"parser",
|
138 |
+
"querypath",
|
139 |
+
"serializer",
|
140 |
+
"xml"
|
141 |
+
],
|
142 |
+
"support": {
|
143 |
+
"issues": "https://github.com/Masterminds/html5-php/issues",
|
144 |
+
"source": "https://github.com/Masterminds/html5-php/tree/2.7.5"
|
145 |
+
},
|
146 |
+
"install-path": "../masterminds/html5"
|
147 |
+
},
|
148 |
{
|
149 |
"name": "phenx/php-font-lib",
|
150 |
"version": "0.5.4",
|
331 |
},
|
332 |
"installation-source": "dist",
|
333 |
"autoload": {
|
|
|
|
|
|
|
334 |
"files": [
|
335 |
"bootstrap.php"
|
336 |
+
],
|
337 |
+
"psr-4": {
|
338 |
+
"Symfony\\Polyfill\\Iconv\\": ""
|
339 |
+
}
|
340 |
},
|
341 |
"notification-url": "https://packagist.org/downloads/",
|
342 |
"license": [
|
464 |
"install-path": "../symfony/polyfill-mbstring"
|
465 |
}
|
466 |
],
|
467 |
+
"dev": true,
|
468 |
"dev-package-names": []
|
469 |
}
|
vendor/composer/installed.php
CHANGED
@@ -5,9 +5,9 @@
|
|
5 |
'type' => 'library',
|
6 |
'install_path' => __DIR__ . '/../../',
|
7 |
'aliases' => array(),
|
8 |
-
'reference' => '
|
9 |
'name' => '__root__',
|
10 |
-
'dev' =>
|
11 |
),
|
12 |
'versions' => array(
|
13 |
'__root__' => array(
|
@@ -16,16 +16,25 @@
|
|
16 |
'type' => 'library',
|
17 |
'install_path' => __DIR__ . '/../../',
|
18 |
'aliases' => array(),
|
19 |
-
'reference' => '
|
20 |
'dev_requirement' => false,
|
21 |
),
|
22 |
'dompdf/dompdf' => array(
|
23 |
-
'pretty_version' => '
|
24 |
-
'version' => '
|
25 |
'type' => 'library',
|
26 |
'install_path' => __DIR__ . '/../dompdf/dompdf',
|
27 |
'aliases' => array(),
|
28 |
-
'reference' => '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
'dev_requirement' => false,
|
30 |
),
|
31 |
'phenx/php-font-lib' => array(
|
5 |
'type' => 'library',
|
6 |
'install_path' => __DIR__ . '/../../',
|
7 |
'aliases' => array(),
|
8 |
+
'reference' => 'de3f055345804a78a51fa043ba01333e0a00fd50',
|
9 |
'name' => '__root__',
|
10 |
+
'dev' => true,
|
11 |
),
|
12 |
'versions' => array(
|
13 |
'__root__' => array(
|
16 |
'type' => 'library',
|
17 |
'install_path' => __DIR__ . '/../../',
|
18 |
'aliases' => array(),
|
19 |
+
'reference' => 'de3f055345804a78a51fa043ba01333e0a00fd50',
|
20 |
'dev_requirement' => false,
|
21 |
),
|
22 |
'dompdf/dompdf' => array(
|
23 |
+
'pretty_version' => 'v2.0.0',
|
24 |
+
'version' => '2.0.0.0',
|
25 |
'type' => 'library',
|
26 |
'install_path' => __DIR__ . '/../dompdf/dompdf',
|
27 |
'aliases' => array(),
|
28 |
+
'reference' => '79573d8b8a141ec8a17312515de8740eed014fa9',
|
29 |
+
'dev_requirement' => false,
|
30 |
+
),
|
31 |
+
'masterminds/html5' => array(
|
32 |
+
'pretty_version' => '2.7.5',
|
33 |
+
'version' => '2.7.5.0',
|
34 |
+
'type' => 'library',
|
35 |
+
'install_path' => __DIR__ . '/../masterminds/html5',
|
36 |
+
'aliases' => array(),
|
37 |
+
'reference' => 'f640ac1bdddff06ea333a920c95bbad8872429ab',
|
38 |
'dev_requirement' => false,
|
39 |
),
|
40 |
'phenx/php-font-lib' => array(
|
vendor/dompdf/dompdf/README.md
CHANGED
@@ -214,12 +214,6 @@ Files accessed through the local file system have the following requirement:
|
|
214 |
|
215 |
## Limitations (Known Issues)
|
216 |
|
217 |
-
* Dompdf is not particularly tolerant to poorly-formed HTML input. To avoid
|
218 |
-
any unexpected rendering issues you should either enable the built-in HTML5
|
219 |
-
parser at runtime (`$options->setIsHtml5ParserEnabled(true);`)
|
220 |
-
or run your HTML through a HTML validator/cleaner (such as
|
221 |
-
[Tidy](http://tidy.sourceforge.net) or the
|
222 |
-
[W3C Markup Validation Service](http://validator.w3.org)).
|
223 |
* Table cells are not pageable, meaning a table row must fit on a single page.
|
224 |
* Elements are rendered on the active page when they are parsed.
|
225 |
* Embedding "raw" SVG's (`<svg><path...></svg>`) isn't working yet, you need to
|
214 |
|
215 |
## Limitations (Known Issues)
|
216 |
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
* Table cells are not pageable, meaning a table row must fit on a single page.
|
218 |
* Elements are rendered on the active page when they are parsed.
|
219 |
* Embedding "raw" SVG's (`<svg><path...></svg>`) isn't working yet, you need to
|
vendor/dompdf/dompdf/VERSION
CHANGED
@@ -1 +1 @@
|
|
1 |
-
|
1 |
+
2.0.0
|
vendor/dompdf/dompdf/composer.json
CHANGED
@@ -35,6 +35,7 @@
|
|
35 |
"php": "^7.1 || ^8.0",
|
36 |
"ext-dom": "*",
|
37 |
"ext-mbstring": "*",
|
|
|
38 |
"phenx/php-font-lib": "^0.5.4",
|
39 |
"phenx/php-svg-lib": "^0.3.3 || ^0.4.0"
|
40 |
},
|
35 |
"php": "^7.1 || ^8.0",
|
36 |
"ext-dom": "*",
|
37 |
"ext-mbstring": "*",
|
38 |
+
"masterminds/html5": "^2.0",
|
39 |
"phenx/php-font-lib": "^0.5.4",
|
40 |
"phenx/php-svg-lib": "^0.3.3 || ^0.4.0"
|
41 |
},
|
vendor/dompdf/dompdf/lib/Cpdf.php
CHANGED
@@ -1048,7 +1048,7 @@ class Cpdf
|
|
1048 |
}
|
1049 |
}
|
1050 |
|
1051 |
-
return 'SUB' . str_pad($base_26, 3
|
1052 |
}
|
1053 |
|
1054 |
/**
|
@@ -1381,7 +1381,7 @@ EOT;
|
|
1381 |
|
1382 |
$res = "\n$id 0 obj\n";
|
1383 |
$res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
|
1384 |
-
$res .= "stream\n" . $stream . "\nendstream" . "\nendobj"
|
1385 |
|
1386 |
return $res;
|
1387 |
}
|
@@ -2423,8 +2423,7 @@ EOT;
|
|
2423 |
$res = "\n$id 0 obj\n<< /Type /XObject\n";
|
2424 |
|
2425 |
foreach ($o["info"] as $k => $v) {
|
2426 |
-
switch($k)
|
2427 |
-
{
|
2428 |
case 'Subtype':
|
2429 |
$res .= "/Subtype /$v\n";
|
2430 |
break;
|
@@ -2477,7 +2476,7 @@ EOT;
|
|
2477 |
}
|
2478 |
|
2479 |
$res .= "/Length " . mb_strlen($tmp, '8bit') . " >>\n";
|
2480 |
-
$res .= "stream\n" . $tmp . "\nendstream" . "\nendobj"
|
2481 |
|
2482 |
return $res;
|
2483 |
}
|
@@ -2515,7 +2514,7 @@ EOT;
|
|
2515 |
$res = "\n$id 0 obj\n<<";
|
2516 |
|
2517 |
foreach ($o["info"] as $k => $v) {
|
2518 |
-
switch($k) {
|
2519 |
case 'Fields':
|
2520 |
$res .= " /Fields [";
|
2521 |
foreach ($v as $i) {
|
@@ -2619,7 +2618,7 @@ EOT;
|
|
2619 |
$res .= ">>\n";
|
2620 |
break;
|
2621 |
case 'T':
|
2622 |
-
if($encrypted) {
|
2623 |
$v = $this->filterText($this->ARC4($v), false, false);
|
2624 |
}
|
2625 |
$res .= "/T ($v)\n";
|
@@ -2662,7 +2661,7 @@ EOT;
|
|
2662 |
$pos = strpos($content, sprintf("/ByteRange [ %'.010d", $id));
|
2663 |
$len = strlen('/ByteRange [ ********** ********** ********** ********** ]');
|
2664 |
$rangeStartPos = $pos + $len + 1 + 10; // before '<'
|
2665 |
-
$content = substr_replace($content, str_pad(sprintf('/ByteRange [ 0 %u %u %u ]', $rangeStartPos, $rangeStartPos + $sign_maxlen + 2, $content_len - 2 - $sign_maxlen - $rangeStartPos
|
2666 |
|
2667 |
$fuid = uniqid();
|
2668 |
$tmpInput = $this->tmp . "/pkcs7.tmp." . $fuid . '.in';
|
@@ -2726,7 +2725,7 @@ EOT;
|
|
2726 |
|
2727 |
$o = &$this->objects[$id];
|
2728 |
foreach ($o['info'] as $k => $v) {
|
2729 |
-
switch($k) {
|
2730 |
case 'Name':
|
2731 |
case 'Location':
|
2732 |
case 'Reason':
|
@@ -2852,7 +2851,7 @@ EOT;
|
|
2852 |
case 'out':
|
2853 |
$res = "\n$id 0 obj << ";
|
2854 |
|
2855 |
-
foreach($this->objects[$id]['info'] as $referenceObjName => $referenceObjId) {
|
2856 |
$res .= "/$referenceObjName $referenceObjId 0 R ";
|
2857 |
}
|
2858 |
|
@@ -3310,11 +3309,11 @@ EOT;
|
|
3310 |
{
|
3311 |
// assume that $font contains the path and file but not the extension
|
3312 |
$name = basename($font);
|
3313 |
-
$dir = dirname($font)
|
3314 |
|
3315 |
$fontcache = $this->fontcache;
|
3316 |
if ($fontcache == '') {
|
3317 |
-
$fontcache =
|
3318 |
}
|
3319 |
|
3320 |
//$name filename without folder and extension of font metrics
|
@@ -3331,36 +3330,22 @@ EOT;
|
|
3331 |
$metrics_name = "$name.ufm";
|
3332 |
}
|
3333 |
|
3334 |
-
$cache_name = "$metrics_name.
|
3335 |
$this->addMessage("metrics: $metrics_name, cache: $cache_name");
|
3336 |
-
|
3337 |
if (file_exists($fontcache . '/' . $cache_name)) {
|
3338 |
-
$this->addMessage("openFont:
|
3339 |
-
$
|
3340 |
-
|
3341 |
-
|
3342 |
-
|
3343 |
-
$this->
|
3344 |
-
$this->fonts[$font] = null;
|
3345 |
-
unset($this->fonts[$font]);
|
3346 |
-
}
|
3347 |
-
} else {
|
3348 |
-
$old_cache_name = "php_$metrics_name";
|
3349 |
-
if (file_exists($fontcache . '/' . $old_cache_name)) {
|
3350 |
-
$this->addMessage(
|
3351 |
-
"openFont: php file doesn't exist $fontcache/$cache_name, creating it from the old format"
|
3352 |
-
);
|
3353 |
-
$old_cache = file_get_contents($fontcache . '/' . $old_cache_name);
|
3354 |
-
file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . $old_cache . ';');
|
3355 |
-
|
3356 |
-
$this->openFont($font);
|
3357 |
-
return;
|
3358 |
}
|
3359 |
}
|
3360 |
|
3361 |
-
if (!isset($this->fonts[$font]) && file_exists($dir
|
3362 |
// then rebuild the php_<font>.afm file from the <font>.afm file
|
3363 |
-
$this->addMessage("openFont: build php file from $dir
|
3364 |
$data = [];
|
3365 |
|
3366 |
// 20 => 'space'
|
@@ -3375,7 +3360,7 @@ EOT;
|
|
3375 |
$cidtogid = str_pad('', 256 * 256 * 2, "\x00");
|
3376 |
}
|
3377 |
|
3378 |
-
$file = file($dir
|
3379 |
|
3380 |
foreach ($file as $rowA) {
|
3381 |
$row = trim($rowA);
|
@@ -3529,7 +3514,7 @@ EOT;
|
|
3529 |
//Because of potential trouble with php safe mode, expect that the folder already exists.
|
3530 |
//If not existing, this will hit performance because of missing cached results.
|
3531 |
if (is_dir($fontcache) && is_writable($fontcache)) {
|
3532 |
-
file_put_contents($fontcache
|
3533 |
}
|
3534 |
$data = null;
|
3535 |
}
|
@@ -3537,8 +3522,6 @@ EOT;
|
|
3537 |
if (!isset($this->fonts[$font])) {
|
3538 |
$this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
|
3539 |
}
|
3540 |
-
|
3541 |
-
//pre_r($this->messages);
|
3542 |
}
|
3543 |
|
3544 |
/**
|
@@ -3548,7 +3531,7 @@ EOT;
|
|
3548 |
* note that encoding='none' will need to be used for symbolic fonts
|
3549 |
* and 'differences' => an array of mappings between numbers 0->255 and character names.
|
3550 |
*
|
3551 |
-
* @param $fontName
|
3552 |
* @param string $encoding
|
3553 |
* @param bool $set
|
3554 |
* @param bool $isSubsetting
|
@@ -3696,8 +3679,8 @@ EOT;
|
|
3696 |
/**
|
3697 |
* sets the color for fill operations
|
3698 |
*
|
3699 |
-
* @param $color
|
3700 |
-
* @param bool
|
3701 |
*/
|
3702 |
function setColor($color, $force = false)
|
3703 |
{
|
@@ -3719,9 +3702,7 @@ EOT;
|
|
3719 |
}
|
3720 |
|
3721 |
/**
|
3722 |
-
*
|
3723 |
-
*
|
3724 |
-
* @param $fillRule
|
3725 |
*/
|
3726 |
function setFillRule($fillRule)
|
3727 |
{
|
@@ -3735,8 +3716,8 @@ EOT;
|
|
3735 |
/**
|
3736 |
* sets the color for stroke operations
|
3737 |
*
|
3738 |
-
* @param $color
|
3739 |
-
* @param bool
|
3740 |
*/
|
3741 |
function setStrokeColor($color, $force = false)
|
3742 |
{
|
@@ -3887,11 +3868,11 @@ EOT;
|
|
3887 |
/**
|
3888 |
* draw a line from one set of coordinates to another
|
3889 |
*
|
3890 |
-
* @param $x1
|
3891 |
-
* @param $y1
|
3892 |
-
* @param $x2
|
3893 |
-
* @param $y2
|
3894 |
-
* @param bool
|
3895 |
*/
|
3896 |
function line($x1, $y1, $x2, $y2, $stroke = true)
|
3897 |
{
|
@@ -3905,14 +3886,14 @@ EOT;
|
|
3905 |
/**
|
3906 |
* draw a bezier curve based on 4 control points
|
3907 |
*
|
3908 |
-
* @param $x0
|
3909 |
-
* @param $y0
|
3910 |
-
* @param $x1
|
3911 |
-
* @param $y1
|
3912 |
-
* @param $x2
|
3913 |
-
* @param $y2
|
3914 |
-
* @param $x3
|
3915 |
-
* @param $y3
|
3916 |
*/
|
3917 |
function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3918 |
{
|
@@ -3926,13 +3907,13 @@ EOT;
|
|
3926 |
/**
|
3927 |
* draw a part of an ellipse
|
3928 |
*
|
3929 |
-
* @param $x0
|
3930 |
-
* @param $y0
|
3931 |
-
* @param $astart
|
3932 |
-
* @param $afinish
|
3933 |
-
* @param $r1
|
3934 |
-
* @param
|
3935 |
-
* @param
|
3936 |
* @param int $nSeg
|
3937 |
*/
|
3938 |
function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
|
@@ -3943,14 +3924,14 @@ EOT;
|
|
3943 |
/**
|
3944 |
* draw a filled ellipse
|
3945 |
*
|
3946 |
-
* @param $x0
|
3947 |
-
* @param $y0
|
3948 |
-
* @param $r1
|
3949 |
-
* @param
|
3950 |
-
* @param
|
3951 |
* @param int $nSeg
|
3952 |
-
* @param
|
3953 |
-
* @param
|
3954 |
*/
|
3955 |
function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
|
3956 |
{
|
@@ -3958,8 +3939,8 @@ EOT;
|
|
3958 |
}
|
3959 |
|
3960 |
/**
|
3961 |
-
* @param $x
|
3962 |
-
* @param $y
|
3963 |
*/
|
3964 |
function lineTo($x, $y)
|
3965 |
{
|
@@ -3967,8 +3948,8 @@ EOT;
|
|
3967 |
}
|
3968 |
|
3969 |
/**
|
3970 |
-
* @param $x
|
3971 |
-
* @param $y
|
3972 |
*/
|
3973 |
function moveTo($x, $y)
|
3974 |
{
|
@@ -3978,12 +3959,12 @@ EOT;
|
|
3978 |
/**
|
3979 |
* draw a bezier curve based on 4 control points
|
3980 |
*
|
3981 |
-
* @param $x1
|
3982 |
-
* @param $y1
|
3983 |
-
* @param $x2
|
3984 |
-
* @param $y2
|
3985 |
-
* @param $x3
|
3986 |
-
* @param $y3
|
3987 |
*/
|
3988 |
function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
|
3989 |
{
|
@@ -3992,6 +3973,11 @@ EOT;
|
|
3992 |
|
3993 |
/**
|
3994 |
* draw a bezier curve based on 4 control points
|
|
|
|
|
|
|
|
|
|
|
3995 |
*/
|
3996 |
function quadTo($cpx, $cpy, $x, $y)
|
3997 |
{
|
@@ -4019,18 +4005,18 @@ EOT;
|
|
4019 |
* nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
|
4020 |
* pretty crappy shape at 2, as we are approximating with bezier curves.
|
4021 |
*
|
4022 |
-
* @param $x0
|
4023 |
-
* @param $y0
|
4024 |
-
* @param $r1
|
4025 |
-
* @param
|
4026 |
-
* @param
|
4027 |
-
* @param int
|
4028 |
-
* @param
|
4029 |
-
* @param
|
4030 |
-
* @param bool
|
4031 |
-
* @param bool
|
4032 |
-
* @param bool
|
4033 |
-
* @param bool
|
4034 |
*/
|
4035 |
function ellipse(
|
4036 |
$x0,
|
@@ -4144,11 +4130,11 @@ EOT;
|
|
4144 |
* (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
|
4145 |
* phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
|
4146 |
*
|
4147 |
-
* @param
|
4148 |
* @param string $cap
|
4149 |
* @param string $join
|
4150 |
-
* @param
|
4151 |
-
* @param int
|
4152 |
*/
|
4153 |
function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
|
4154 |
{
|
@@ -4182,19 +4168,19 @@ EOT;
|
|
4182 |
/**
|
4183 |
* draw a polygon, the syntax for this is similar to the GD polygon command
|
4184 |
*
|
4185 |
-
* @param $p
|
4186 |
-
* @param $
|
4187 |
-
* @param bool $f
|
4188 |
*/
|
4189 |
-
function polygon($p,
|
4190 |
{
|
4191 |
$this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
|
4192 |
|
4193 |
-
|
|
|
4194 |
$this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
|
4195 |
}
|
4196 |
|
4197 |
-
if ($
|
4198 |
$this->addContent(' f');
|
4199 |
} else {
|
4200 |
$this->addContent(' S');
|
@@ -4205,10 +4191,10 @@ EOT;
|
|
4205 |
* a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
4206 |
* the coordinates of the upper-right corner
|
4207 |
*
|
4208 |
-
* @param $x1
|
4209 |
-
* @param $y1
|
4210 |
-
* @param $width
|
4211 |
-
* @param $height
|
4212 |
*/
|
4213 |
function filledRectangle($x1, $y1, $width, $height)
|
4214 |
{
|
@@ -4219,10 +4205,10 @@ EOT;
|
|
4219 |
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
4220 |
* the coordinates of the upper-right corner
|
4221 |
*
|
4222 |
-
* @param $x1
|
4223 |
-
* @param $y1
|
4224 |
-
* @param $width
|
4225 |
-
* @param $height
|
4226 |
*/
|
4227 |
function rectangle($x1, $y1, $width, $height)
|
4228 |
{
|
@@ -4233,10 +4219,10 @@ EOT;
|
|
4233 |
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
4234 |
* the coordinates of the upper-right corner
|
4235 |
*
|
4236 |
-
* @param $x1
|
4237 |
-
* @param $y1
|
4238 |
-
* @param $width
|
4239 |
-
* @param $height
|
4240 |
*/
|
4241 |
function rect($x1, $y1, $width, $height)
|
4242 |
{
|
@@ -4348,8 +4334,10 @@ EOT;
|
|
4348 |
$color = implode(' ', $color) . ' rg';
|
4349 |
|
4350 |
$currentFontNum = $this->currentFontNum;
|
4351 |
-
$font = array_filter(
|
4352 |
-
|
|
|
|
|
4353 |
|
4354 |
$this->o_acroform($this->acroFormId, 'font',
|
4355 |
['objNum' => $font[0]['objNum'], 'fontNum' => $font[0]['fontNum']]);
|
@@ -4464,10 +4452,10 @@ EOT;
|
|
4464 |
/**
|
4465 |
* draw a clipping rectangle, all the elements added after this will be clipped
|
4466 |
*
|
4467 |
-
* @param $x1
|
4468 |
-
* @param $y1
|
4469 |
-
* @param $width
|
4470 |
-
* @param $height
|
4471 |
*/
|
4472 |
function clippingRectangle($x1, $y1, $width, $height)
|
4473 |
{
|
@@ -4478,14 +4466,14 @@ EOT;
|
|
4478 |
/**
|
4479 |
* draw a clipping rounded rectangle, all the elements added after this will be clipped
|
4480 |
*
|
4481 |
-
* @param $x1
|
4482 |
-
* @param $y1
|
4483 |
-
* @param $w
|
4484 |
-
* @param $h
|
4485 |
-
* @param $rTL
|
4486 |
-
* @param $rTR
|
4487 |
-
* @param $rBR
|
4488 |
-
* @param $rBL
|
4489 |
*/
|
4490 |
function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
4491 |
{
|
@@ -4525,6 +4513,25 @@ EOT;
|
|
4525 |
$this->addContent(" W n");
|
4526 |
}
|
4527 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4528 |
/**
|
4529 |
* ends the last clipping shape
|
4530 |
*/
|
@@ -4741,10 +4748,11 @@ EOT;
|
|
4741 |
/**
|
4742 |
* return the height in units of the current font in the given size
|
4743 |
*
|
4744 |
-
* @param $size
|
4745 |
-
*
|
|
|
4746 |
*/
|
4747 |
-
function getFontHeight($size)
|
4748 |
{
|
4749 |
if (!$this->numFonts) {
|
4750 |
$this->selectFont($this->defaultFont);
|
@@ -4776,10 +4784,11 @@ EOT;
|
|
4776 |
}
|
4777 |
|
4778 |
/**
|
4779 |
-
* @param $size
|
4780 |
-
*
|
|
|
4781 |
*/
|
4782 |
-
function getFontXHeight($size)
|
4783 |
{
|
4784 |
if (!$this->numFonts) {
|
4785 |
$this->selectFont($this->defaultFont);
|
@@ -4802,10 +4811,11 @@ EOT;
|
|
4802 |
* if you add this number to the baseline, you get the level of the bottom of the font
|
4803 |
* it is in the pdf user units
|
4804 |
*
|
4805 |
-
* @param $size
|
4806 |
-
*
|
|
|
4807 |
*/
|
4808 |
-
function getFontDescender($size)
|
4809 |
{
|
4810 |
// note that this will most likely return a negative value
|
4811 |
if (!$this->numFonts) {
|
@@ -4843,7 +4853,7 @@ EOT;
|
|
4843 |
//$text = html_entity_decode($text, ENT_QUOTES);
|
4844 |
$text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
|
4845 |
}
|
4846 |
-
}
|
4847 |
$text = $this->utf8toUtf16BE($text, $bom);
|
4848 |
}
|
4849 |
|
@@ -5014,8 +5024,8 @@ EOT;
|
|
5014 |
/**
|
5015 |
* register text for font subsetting
|
5016 |
*
|
5017 |
-
* @param $font
|
5018 |
-
* @param $text
|
5019 |
*/
|
5020 |
function registerText($font, $text)
|
5021 |
{
|
@@ -5035,14 +5045,14 @@ EOT;
|
|
5035 |
/**
|
5036 |
* add text to the document, at a specified location, size and angle on the page
|
5037 |
*
|
5038 |
-
* @param $x
|
5039 |
-
* @param $y
|
5040 |
-
* @param $size
|
5041 |
-
* @param $text
|
5042 |
-
* @param
|
5043 |
-
* @param
|
5044 |
-
* @param
|
5045 |
-
* @param bool
|
5046 |
*/
|
5047 |
function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
|
5048 |
{
|
@@ -5155,13 +5165,14 @@ EOT;
|
|
5155 |
* calculate how wide a given text string will be on a page, at a given size.
|
5156 |
* this can be called externally, but is also used by the other class functions
|
5157 |
*
|
5158 |
-
* @param float
|
5159 |
* @param string $text
|
5160 |
-
* @param float
|
5161 |
-
* @param float
|
|
|
5162 |
* @return float
|
5163 |
*/
|
5164 |
-
function getTextWidth($size, $text, $
|
5165 |
{
|
5166 |
static $ord_cache = [];
|
5167 |
|
@@ -5176,9 +5187,6 @@ EOT;
|
|
5176 |
|
5177 |
$text = str_replace(["\r", "\n"], "", $text);
|
5178 |
|
5179 |
-
// converts a number or a float to a string so it can get the width
|
5180 |
-
$text = "$text";
|
5181 |
-
|
5182 |
// hmm, this is where it all starts to get tricky - use the font information to
|
5183 |
// calculate the width of each character, add them up and convert to user units
|
5184 |
$w = 0;
|
@@ -5205,14 +5213,14 @@ EOT;
|
|
5205 |
|
5206 |
// add additional padding for space
|
5207 |
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
5208 |
-
$w += $
|
5209 |
}
|
5210 |
}
|
5211 |
}
|
5212 |
|
5213 |
// add additional char spacing
|
5214 |
-
if ($
|
5215 |
-
$w += $
|
5216 |
}
|
5217 |
|
5218 |
} else {
|
@@ -5240,14 +5248,14 @@ EOT;
|
|
5240 |
|
5241 |
// add additional padding for space
|
5242 |
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
5243 |
-
$w += $
|
5244 |
}
|
5245 |
}
|
5246 |
}
|
5247 |
|
5248 |
// add additional char spacing
|
5249 |
-
if ($
|
5250 |
-
$w += $
|
5251 |
}
|
5252 |
}
|
5253 |
|
@@ -5487,12 +5495,12 @@ EOT;
|
|
5487 |
}
|
5488 |
|
5489 |
/**
|
5490 |
-
*
|
5491 |
*
|
5492 |
-
* @param $label
|
5493 |
-
* @param
|
5494 |
*/
|
5495 |
-
function addInfo($label, $value =
|
5496 |
{
|
5497 |
// this will only work if the label is one of the valid ones.
|
5498 |
// modify this so that arrays can be passed as well.
|
@@ -5500,7 +5508,7 @@ EOT;
|
|
5500 |
// else assume that they are both scalar, anything else will probably error
|
5501 |
if (is_array($label)) {
|
5502 |
foreach ($label as $l => $v) {
|
5503 |
-
$this->o_info($this->infoObject, $l, $v);
|
5504 |
}
|
5505 |
} else {
|
5506 |
$this->o_info($this->infoObject, $label, $value);
|
1048 |
}
|
1049 |
}
|
1050 |
|
1051 |
+
return 'SUB' . str_pad($base_26, 3, 'A', STR_PAD_LEFT);
|
1052 |
}
|
1053 |
|
1054 |
/**
|
1381 |
|
1382 |
$res = "\n$id 0 obj\n";
|
1383 |
$res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
|
1384 |
+
$res .= "stream\n" . $stream . "\nendstream" . "\nendobj";
|
1385 |
|
1386 |
return $res;
|
1387 |
}
|
2423 |
$res = "\n$id 0 obj\n<< /Type /XObject\n";
|
2424 |
|
2425 |
foreach ($o["info"] as $k => $v) {
|
2426 |
+
switch ($k) {
|
|
|
2427 |
case 'Subtype':
|
2428 |
$res .= "/Subtype /$v\n";
|
2429 |
break;
|
2476 |
}
|
2477 |
|
2478 |
$res .= "/Length " . mb_strlen($tmp, '8bit') . " >>\n";
|
2479 |
+
$res .= "stream\n" . $tmp . "\nendstream" . "\nendobj";
|
2480 |
|
2481 |
return $res;
|
2482 |
}
|
2514 |
$res = "\n$id 0 obj\n<<";
|
2515 |
|
2516 |
foreach ($o["info"] as $k => $v) {
|
2517 |
+
switch ($k) {
|
2518 |
case 'Fields':
|
2519 |
$res .= " /Fields [";
|
2520 |
foreach ($v as $i) {
|
2618 |
$res .= ">>\n";
|
2619 |
break;
|
2620 |
case 'T':
|
2621 |
+
if ($encrypted) {
|
2622 |
$v = $this->filterText($this->ARC4($v), false, false);
|
2623 |
}
|
2624 |
$res .= "/T ($v)\n";
|
2661 |
$pos = strpos($content, sprintf("/ByteRange [ %'.010d", $id));
|
2662 |
$len = strlen('/ByteRange [ ********** ********** ********** ********** ]');
|
2663 |
$rangeStartPos = $pos + $len + 1 + 10; // before '<'
|
2664 |
+
$content = substr_replace($content, str_pad(sprintf('/ByteRange [ 0 %u %u %u ]', $rangeStartPos, $rangeStartPos + $sign_maxlen + 2, $content_len - 2 - $sign_maxlen - $rangeStartPos), $len, ' ', STR_PAD_RIGHT), $pos, $len);
|
2665 |
|
2666 |
$fuid = uniqid();
|
2667 |
$tmpInput = $this->tmp . "/pkcs7.tmp." . $fuid . '.in';
|
2725 |
|
2726 |
$o = &$this->objects[$id];
|
2727 |
foreach ($o['info'] as $k => $v) {
|
2728 |
+
switch ($k) {
|
2729 |
case 'Name':
|
2730 |
case 'Location':
|
2731 |
case 'Reason':
|
2851 |
case 'out':
|
2852 |
$res = "\n$id 0 obj << ";
|
2853 |
|
2854 |
+
foreach ($this->objects[$id]['info'] as $referenceObjName => $referenceObjId) {
|
2855 |
$res .= "/$referenceObjName $referenceObjId 0 R ";
|
2856 |
}
|
2857 |
|
3309 |
{
|
3310 |
// assume that $font contains the path and file but not the extension
|
3311 |
$name = basename($font);
|
3312 |
+
$dir = dirname($font);
|
3313 |
|
3314 |
$fontcache = $this->fontcache;
|
3315 |
if ($fontcache == '') {
|
3316 |
+
$fontcache = $dir;
|
3317 |
}
|
3318 |
|
3319 |
//$name filename without folder and extension of font metrics
|
3330 |
$metrics_name = "$name.ufm";
|
3331 |
}
|
3332 |
|
3333 |
+
$cache_name = "$metrics_name.json";
|
3334 |
$this->addMessage("metrics: $metrics_name, cache: $cache_name");
|
3335 |
+
|
3336 |
if (file_exists($fontcache . '/' . $cache_name)) {
|
3337 |
+
$this->addMessage("openFont: json metrics file exists $fontcache/$cache_name");
|
3338 |
+
$cached_font_info = json_decode(file_get_contents($fontcache . '/' . $cache_name), true);
|
3339 |
+
if (!isset($cached_font_info['_version_']) || $cached_font_info['_version_'] != $this->fontcacheVersion) {
|
3340 |
+
$this->addMessage('openFont: font cache is out of date, regenerating');
|
3341 |
+
} else {
|
3342 |
+
$this->fonts[$font] = $cached_font_info;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3343 |
}
|
3344 |
}
|
3345 |
|
3346 |
+
if (!isset($this->fonts[$font]) && file_exists("$dir/$metrics_name")) {
|
3347 |
// then rebuild the php_<font>.afm file from the <font>.afm file
|
3348 |
+
$this->addMessage("openFont: build php file from $dir/$metrics_name");
|
3349 |
$data = [];
|
3350 |
|
3351 |
// 20 => 'space'
|
3360 |
$cidtogid = str_pad('', 256 * 256 * 2, "\x00");
|
3361 |
}
|
3362 |
|
3363 |
+
$file = file("$dir/$metrics_name");
|
3364 |
|
3365 |
foreach ($file as $rowA) {
|
3366 |
$row = trim($rowA);
|
3514 |
//Because of potential trouble with php safe mode, expect that the folder already exists.
|
3515 |
//If not existing, this will hit performance because of missing cached results.
|
3516 |
if (is_dir($fontcache) && is_writable($fontcache)) {
|
3517 |
+
file_put_contents("$fontcache/$cache_name", json_encode($data, JSON_PRETTY_PRINT));
|
3518 |
}
|
3519 |
$data = null;
|
3520 |
}
|
3522 |
if (!isset($this->fonts[$font])) {
|
3523 |
$this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
|
3524 |
}
|
|
|
|
|
3525 |
}
|
3526 |
|
3527 |
/**
|
3531 |
* note that encoding='none' will need to be used for symbolic fonts
|
3532 |
* and 'differences' => an array of mappings between numbers 0->255 and character names.
|
3533 |
*
|
3534 |
+
* @param string $fontName
|
3535 |
* @param string $encoding
|
3536 |
* @param bool $set
|
3537 |
* @param bool $isSubsetting
|
3679 |
/**
|
3680 |
* sets the color for fill operations
|
3681 |
*
|
3682 |
+
* @param array $color
|
3683 |
+
* @param bool $force
|
3684 |
*/
|
3685 |
function setColor($color, $force = false)
|
3686 |
{
|
3702 |
}
|
3703 |
|
3704 |
/**
|
3705 |
+
* @param string $fillRule
|
|
|
|
|
3706 |
*/
|
3707 |
function setFillRule($fillRule)
|
3708 |
{
|
3716 |
/**
|
3717 |
* sets the color for stroke operations
|
3718 |
*
|
3719 |
+
* @param array $color
|
3720 |
+
* @param bool $force
|
3721 |
*/
|
3722 |
function setStrokeColor($color, $force = false)
|
3723 |
{
|
3868 |
/**
|
3869 |
* draw a line from one set of coordinates to another
|
3870 |
*
|
3871 |
+
* @param float $x1
|
3872 |
+
* @param float $y1
|
3873 |
+
* @param float $x2
|
3874 |
+
* @param float $y2
|
3875 |
+
* @param bool $stroke
|
3876 |
*/
|
3877 |
function line($x1, $y1, $x2, $y2, $stroke = true)
|
3878 |
{
|
3886 |
/**
|
3887 |
* draw a bezier curve based on 4 control points
|
3888 |
*
|
3889 |
+
* @param float $x0
|
3890 |
+
* @param float $y0
|
3891 |
+
* @param float $x1
|
3892 |
+
* @param float $y1
|
3893 |
+
* @param float $x2
|
3894 |
+
* @param float $y2
|
3895 |
+
* @param float $x3
|
3896 |
+
* @param float $y3
|
3897 |
*/
|
3898 |
function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
3899 |
{
|
3907 |
/**
|
3908 |
* draw a part of an ellipse
|
3909 |
*
|
3910 |
+
* @param float $x0
|
3911 |
+
* @param float $y0
|
3912 |
+
* @param float $astart
|
3913 |
+
* @param float $afinish
|
3914 |
+
* @param float $r1
|
3915 |
+
* @param float $r2
|
3916 |
+
* @param float $angle
|
3917 |
* @param int $nSeg
|
3918 |
*/
|
3919 |
function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
|
3924 |
/**
|
3925 |
* draw a filled ellipse
|
3926 |
*
|
3927 |
+
* @param float $x0
|
3928 |
+
* @param float $y0
|
3929 |
+
* @param float $r1
|
3930 |
+
* @param float $r2
|
3931 |
+
* @param float $angle
|
3932 |
* @param int $nSeg
|
3933 |
+
* @param float $astart
|
3934 |
+
* @param float $afinish
|
3935 |
*/
|
3936 |
function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
|
3937 |
{
|
3939 |
}
|
3940 |
|
3941 |
/**
|
3942 |
+
* @param float $x
|
3943 |
+
* @param float $y
|
3944 |
*/
|
3945 |
function lineTo($x, $y)
|
3946 |
{
|
3948 |
}
|
3949 |
|
3950 |
/**
|
3951 |
+
* @param float $x
|
3952 |
+
* @param float $y
|
3953 |
*/
|
3954 |
function moveTo($x, $y)
|
3955 |
{
|
3959 |
/**
|
3960 |
* draw a bezier curve based on 4 control points
|
3961 |
*
|
3962 |
+
* @param float $x1
|
3963 |
+
* @param float $y1
|
3964 |
+
* @param float $x2
|
3965 |
+
* @param float $y2
|
3966 |
+
* @param float $x3
|
3967 |
+
* @param float $y3
|
3968 |
*/
|
3969 |
function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
|
3970 |
{
|
3973 |
|
3974 |
/**
|
3975 |
* draw a bezier curve based on 4 control points
|
3976 |
+
*
|
3977 |
+
* @param float $cpx
|
3978 |
+
* @param float $cpy
|
3979 |
+
* @param float $x
|
3980 |
+
* @param float $y
|
3981 |
*/
|
3982 |
function quadTo($cpx, $cpy, $x, $y)
|
3983 |
{
|
4005 |
* nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
|
4006 |
* pretty crappy shape at 2, as we are approximating with bezier curves.
|
4007 |
*
|
4008 |
+
* @param float $x0
|
4009 |
+
* @param float $y0
|
4010 |
+
* @param float $r1
|
4011 |
+
* @param float $r2
|
4012 |
+
* @param float $angle
|
4013 |
+
* @param int $nSeg
|
4014 |
+
* @param float $astart
|
4015 |
+
* @param float $afinish
|
4016 |
+
* @param bool $close
|
4017 |
+
* @param bool $fill
|
4018 |
+
* @param bool $stroke
|
4019 |
+
* @param bool $incomplete
|
4020 |
*/
|
4021 |
function ellipse(
|
4022 |
$x0,
|
4130 |
* (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
|
4131 |
* phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
|
4132 |
*
|
4133 |
+
* @param float $width
|
4134 |
* @param string $cap
|
4135 |
* @param string $join
|
4136 |
+
* @param array $dash
|
4137 |
+
* @param int $phase
|
4138 |
*/
|
4139 |
function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
|
4140 |
{
|
4168 |
/**
|
4169 |
* draw a polygon, the syntax for this is similar to the GD polygon command
|
4170 |
*
|
4171 |
+
* @param float[] $p
|
4172 |
+
* @param bool $fill
|
|
|
4173 |
*/
|
4174 |
+
public function polygon(array $p, bool $fill = false): void
|
4175 |
{
|
4176 |
$this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
|
4177 |
|
4178 |
+
$n = count($p);
|
4179 |
+
for ($i = 2; $i < $n; $i = $i + 2) {
|
4180 |
$this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
|
4181 |
}
|
4182 |
|
4183 |
+
if ($fill) {
|
4184 |
$this->addContent(' f');
|
4185 |
} else {
|
4186 |
$this->addContent(' S');
|
4191 |
* a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
4192 |
* the coordinates of the upper-right corner
|
4193 |
*
|
4194 |
+
* @param float $x1
|
4195 |
+
* @param float $y1
|
4196 |
+
* @param float $width
|
4197 |
+
* @param float $height
|
4198 |
*/
|
4199 |
function filledRectangle($x1, $y1, $width, $height)
|
4200 |
{
|
4205 |
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
4206 |
* the coordinates of the upper-right corner
|
4207 |
*
|
4208 |
+
* @param float $x1
|
4209 |
+
* @param float $y1
|
4210 |
+
* @param float $width
|
4211 |
+
* @param float $height
|
4212 |
*/
|
4213 |
function rectangle($x1, $y1, $width, $height)
|
4214 |
{
|
4219 |
* draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
|
4220 |
* the coordinates of the upper-right corner
|
4221 |
*
|
4222 |
+
* @param float $x1
|
4223 |
+
* @param float $y1
|
4224 |
+
* @param float $width
|
4225 |
+
* @param float $height
|
4226 |
*/
|
4227 |
function rect($x1, $y1, $width, $height)
|
4228 |
{
|
4334 |
$color = implode(' ', $color) . ' rg';
|
4335 |
|
4336 |
$currentFontNum = $this->currentFontNum;
|
4337 |
+
$font = array_filter(
|
4338 |
+
$this->objects[$this->currentNode]['info']['fonts'],
|
4339 |
+
function ($item) use ($currentFontNum) { return $item['fontNum'] == $currentFontNum; }
|
4340 |
+
);
|
4341 |
|
4342 |
$this->o_acroform($this->acroFormId, 'font',
|
4343 |
['objNum' => $font[0]['objNum'], 'fontNum' => $font[0]['fontNum']]);
|
4452 |
/**
|
4453 |
* draw a clipping rectangle, all the elements added after this will be clipped
|
4454 |
*
|
4455 |
+
* @param float $x1
|
4456 |
+
* @param float $y1
|
4457 |
+
* @param float $width
|
4458 |
+
* @param float $height
|
4459 |
*/
|
4460 |
function clippingRectangle($x1, $y1, $width, $height)
|
4461 |
{
|
4466 |
/**
|
4467 |
* draw a clipping rounded rectangle, all the elements added after this will be clipped
|
4468 |
*
|
4469 |
+
* @param float $x1
|
4470 |
+
* @param float $y1
|
4471 |
+
* @param float $w
|
4472 |
+
* @param float $h
|
4473 |
+
* @param float $rTL
|
4474 |
+
* @param float $rTR
|
4475 |
+
* @param float $rBR
|
4476 |
+
* @param float $rBL
|
4477 |
*/
|
4478 |
function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
4479 |
{
|
4513 |
$this->addContent(" W n");
|
4514 |
}
|
4515 |
|
4516 |
+
/**
|
4517 |
+
* draw a clipping polygon, the syntax for this is similar to the GD polygon command
|
4518 |
+
*
|
4519 |
+
* @param float[] $p
|
4520 |
+
*/
|
4521 |
+
public function clippingPolygon(array $p): void
|
4522 |
+
{
|
4523 |
+
$this->save();
|
4524 |
+
|
4525 |
+
$this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
|
4526 |
+
|
4527 |
+
$n = count($p);
|
4528 |
+
for ($i = 2; $i < $n; $i = $i + 2) {
|
4529 |
+
$this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
|
4530 |
+
}
|
4531 |
+
|
4532 |
+
$this->addContent("W n");
|
4533 |
+
}
|
4534 |
+
|
4535 |
/**
|
4536 |
* ends the last clipping shape
|
4537 |
*/
|
4748 |
/**
|
4749 |
* return the height in units of the current font in the given size
|
4750 |
*
|
4751 |
+
* @param float $size
|
4752 |
+
*
|
4753 |
+
* @return float
|
4754 |
*/
|
4755 |
+
public function getFontHeight(float $size): float
|
4756 |
{
|
4757 |
if (!$this->numFonts) {
|
4758 |
$this->selectFont($this->defaultFont);
|
4784 |
}
|
4785 |
|
4786 |
/**
|
4787 |
+
* @param float $size
|
4788 |
+
*
|
4789 |
+
* @return float
|
4790 |
*/
|
4791 |
+
public function getFontXHeight(float $size): float
|
4792 |
{
|
4793 |
if (!$this->numFonts) {
|
4794 |
$this->selectFont($this->defaultFont);
|
4811 |
* if you add this number to the baseline, you get the level of the bottom of the font
|
4812 |
* it is in the pdf user units
|
4813 |
*
|
4814 |
+
* @param float $size
|
4815 |
+
*
|
4816 |
+
* @return float
|
4817 |
*/
|
4818 |
+
public function getFontDescender(float $size): float
|
4819 |
{
|
4820 |
// note that this will most likely return a negative value
|
4821 |
if (!$this->numFonts) {
|
4853 |
//$text = html_entity_decode($text, ENT_QUOTES);
|
4854 |
$text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
|
4855 |
}
|
4856 |
+
} elseif ($bom) {
|
4857 |
$text = $this->utf8toUtf16BE($text, $bom);
|
4858 |
}
|
4859 |
|
5024 |
/**
|
5025 |
* register text for font subsetting
|
5026 |
*
|
5027 |
+
* @param string $font
|
5028 |
+
* @param string $text
|
5029 |
*/
|
5030 |
function registerText($font, $text)
|
5031 |
{
|
5045 |
/**
|
5046 |
* add text to the document, at a specified location, size and angle on the page
|
5047 |
*
|
5048 |
+
* @param float $x
|
5049 |
+
* @param float $y
|
5050 |
+
* @param float $size
|
5051 |
+
* @param string $text
|
5052 |
+
* @param float $angle
|
5053 |
+
* @param float $wordSpaceAdjust
|
5054 |
+
* @param float $charSpaceAdjust
|
5055 |
+
* @param bool $smallCaps
|
5056 |
*/
|
5057 |
function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
|
5058 |
{
|
5165 |
* calculate how wide a given text string will be on a page, at a given size.
|
5166 |
* this can be called externally, but is also used by the other class functions
|
5167 |
*
|
5168 |
+
* @param float $size
|
5169 |
* @param string $text
|
5170 |
+
* @param float $wordSpacing
|
5171 |
+
* @param float $charSpacing
|
5172 |
+
*
|
5173 |
* @return float
|
5174 |
*/
|
5175 |
+
public function getTextWidth(float $size, string $text, float $wordSpacing = 0.0, float $charSpacing = 0.0): float
|
5176 |
{
|
5177 |
static $ord_cache = [];
|
5178 |
|
5187 |
|
5188 |
$text = str_replace(["\r", "\n"], "", $text);
|
5189 |
|
|
|
|
|
|
|
5190 |
// hmm, this is where it all starts to get tricky - use the font information to
|
5191 |
// calculate the width of each character, add them up and convert to user units
|
5192 |
$w = 0;
|
5213 |
|
5214 |
// add additional padding for space
|
5215 |
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
5216 |
+
$w += $wordSpacing * $space_scale;
|
5217 |
}
|
5218 |
}
|
5219 |
}
|
5220 |
|
5221 |
// add additional char spacing
|
5222 |
+
if ($charSpacing != 0) {
|
5223 |
+
$w += $charSpacing * $space_scale * count($unicode);
|
5224 |
}
|
5225 |
|
5226 |
} else {
|
5248 |
|
5249 |
// add additional padding for space
|
5250 |
if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space
|
5251 |
+
$w += $wordSpacing * $space_scale;
|
5252 |
}
|
5253 |
}
|
5254 |
}
|
5255 |
|
5256 |
// add additional char spacing
|
5257 |
+
if ($charSpacing != 0) {
|
5258 |
+
$w += $charSpacing * $space_scale * $len;
|
5259 |
}
|
5260 |
}
|
5261 |
|
5495 |
}
|
5496 |
|
5497 |
/**
|
5498 |
+
* Add content to the documents info object
|
5499 |
*
|
5500 |
+
* @param string|array $label
|
5501 |
+
* @param string $value
|
5502 |
*/
|
5503 |
+
public function addInfo($label, string $value = ""): void
|
5504 |
{
|
5505 |
// this will only work if the label is one of the valid ones.
|
5506 |
// modify this so that arrays can be passed as well.
|
5508 |
// else assume that they are both scalar, anything else will probably error
|
5509 |
if (is_array($label)) {
|
5510 |
foreach ($label as $l => $v) {
|
5511 |
+
$this->o_info($this->infoObject, $l, (string) $v);
|
5512 |
}
|
5513 |
} else {
|
5514 |
$this->o_info($this->infoObject, $label, $value);
|
vendor/dompdf/dompdf/lib/fonts/dompdf_font_family_cache.dist.php
DELETED
@@ -1,97 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
return function ($rootDir) {
|
3 |
-
$distFontDir = $rootDir . '/lib/fonts';
|
4 |
-
return [
|
5 |
-
'sans-serif' =>
|
6 |
-
[
|
7 |
-
'normal' => $distFontDir . '/Helvetica',
|
8 |
-
'bold' => $distFontDir . '/Helvetica-Bold',
|
9 |
-
'italic' => $distFontDir . '/Helvetica-Oblique',
|
10 |
-
'bold_italic' => $distFontDir . '/Helvetica-BoldOblique'
|
11 |
-
],
|
12 |
-
'times' =>
|
13 |
-
[
|
14 |
-
'normal' => $distFontDir . '/Times-Roman',
|
15 |
-
'bold' => $distFontDir . '/Times-Bold',
|
16 |
-
'italic' => $distFontDir . '/Times-Italic',
|
17 |
-
'bold_italic' => $distFontDir . '/Times-BoldItalic'
|
18 |
-
],
|
19 |
-
'times-roman' =>
|
20 |
-
[
|
21 |
-
'normal' => $distFontDir . '/Times-Roman',
|
22 |
-
'bold' => $distFontDir . '/Times-Bold',
|
23 |
-
'italic' => $distFontDir . '/Times-Italic',
|
24 |
-
'bold_italic' => $distFontDir . '/Times-BoldItalic'
|
25 |
-
],
|
26 |
-
'courier' =>
|
27 |
-
[
|
28 |
-
'normal' => $distFontDir . '/Courier',
|
29 |
-
'bold' => $distFontDir . '/Courier-Bold',
|
30 |
-
'italic' => $distFontDir . '/Courier-Oblique',
|
31 |
-
'bold_italic' => $distFontDir . '/Courier-BoldOblique'
|
32 |
-
],
|
33 |
-
'helvetica' =>
|
34 |
-
[
|
35 |
-
'normal' => $distFontDir . '/Helvetica',
|
36 |
-
'bold' => $distFontDir . '/Helvetica-Bold',
|
37 |
-
'italic' => $distFontDir . '/Helvetica-Oblique',
|
38 |
-
'bold_italic' => $distFontDir . '/Helvetica-BoldOblique'
|
39 |
-
],
|
40 |
-
'zapfdingbats' =>
|
41 |
-
[
|
42 |
-
'normal' => $distFontDir . '/ZapfDingbats',
|
43 |
-
'bold' => $distFontDir . '/ZapfDingbats',
|
44 |
-
'italic' => $distFontDir . '/ZapfDingbats',
|
45 |
-
'bold_italic' => $distFontDir . '/ZapfDingbats'
|
46 |
-
],
|
47 |
-
'symbol' =>
|
48 |
-
[
|
49 |
-
'normal' => $distFontDir . '/Symbol',
|
50 |
-
'bold' => $distFontDir . '/Symbol',
|
51 |
-
'italic' => $distFontDir . '/Symbol',
|
52 |
-
'bold_italic' => $distFontDir . '/Symbol'
|
53 |
-
],
|
54 |
-
'serif' =>
|
55 |
-
[
|
56 |
-
'normal' => $distFontDir . '/Times-Roman',
|
57 |
-
'bold' => $distFontDir . '/Times-Bold',
|
58 |
-
'italic' => $distFontDir . '/Times-Italic',
|
59 |
-
'bold_italic' => $distFontDir . '/Times-BoldItalic'
|
60 |
-
],
|
61 |
-
'monospace' =>
|
62 |
-
[
|
63 |
-
'normal' => $distFontDir . '/Courier',
|
64 |
-
'bold' => $distFontDir . '/Courier-Bold',
|
65 |
-
'italic' => $distFontDir . '/Courier-Oblique',
|
66 |
-
'bold_italic' => $distFontDir . '/Courier-BoldOblique'
|
67 |
-
],
|
68 |
-
'fixed' =>
|
69 |
-
[
|
70 |
-
'normal' => $distFontDir . '/Courier',
|
71 |
-
'bold' => $distFontDir . '/Courier-Bold',
|
72 |
-
'italic' => $distFontDir . '/Courier-Oblique',
|
73 |
-
'bold_italic' => $distFontDir . '/Courier-BoldOblique'
|
74 |
-
],
|
75 |
-
'dejavu sans' =>
|
76 |
-
[
|
77 |
-
'bold' => $distFontDir . '/DejaVuSans-Bold',
|
78 |
-
'bold_italic' => $distFontDir . '/DejaVuSans-BoldOblique',
|
79 |
-
'italic' => $distFontDir . '/DejaVuSans-Oblique',
|
80 |
-
'normal' => $distFontDir . '/DejaVuSans'
|
81 |
-
],
|
82 |
-
'dejavu sans mono' =>
|
83 |
-
[
|
84 |
-
'bold' => $distFontDir . '/DejaVuSansMono-Bold',
|
85 |
-
'bold_italic' => $distFontDir . '/DejaVuSansMono-BoldOblique',
|
86 |
-
'italic' => $distFontDir . '/DejaVuSansMono-Oblique',
|
87 |
-
'normal' => $distFontDir . '/DejaVuSansMono'
|
88 |
-
],
|
89 |
-
'dejavu serif' =>
|
90 |
-
[
|
91 |
-
'bold' => $distFontDir . '/DejaVuSerif-Bold',
|
92 |
-
'bold_italic' => $distFontDir . '/DejaVuSerif-BoldItalic',
|
93 |
-
'italic' => $distFontDir . '/DejaVuSerif-Italic',
|
94 |
-
'normal' => $distFontDir . '/DejaVuSerif'
|
95 |
-
]
|
96 |
-
];
|
97 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"sans-serif": {
|
3 |
+
"normal": "Helvetica",
|
4 |
+
"bold": "Helvetica-Bold",
|
5 |
+
"italic": "Helvetica-Oblique",
|
6 |
+
"bold_italic": "Helvetica-BoldOblique"
|
7 |
+
},
|
8 |
+
"times": {
|
9 |
+
"normal": "Times-Roman",
|
10 |
+
"bold": "Times-Bold",
|
11 |
+
"italic": "Times-Italic",
|
12 |
+
"bold_italic": "Times-BoldItalic"
|
13 |
+
},
|
14 |
+
"times-roman": {
|
15 |
+
"normal": "Times-Roman",
|
16 |
+
"bold": "Times-Bold",
|
17 |
+
"italic": "Times-Italic",
|
18 |
+
"bold_italic": "Times-BoldItalic"
|
19 |
+
},
|
20 |
+
"courier": {
|
21 |
+
"normal": "Courier",
|
22 |
+
"bold": "Courier-Bold",
|
23 |
+
"italic": "Courier-Oblique",
|
24 |
+
"bold_italic": "Courier-BoldOblique"
|
25 |
+
},
|
26 |
+
"helvetica": {
|
27 |
+
"normal": "Helvetica",
|
28 |
+
"bold": "Helvetica-Bold",
|
29 |
+
"italic": "Helvetica-Oblique",
|
30 |
+
"bold_italic": "Helvetica-BoldOblique"
|
31 |
+
},
|
32 |
+
"zapfdingbats": {
|
33 |
+
"normal": "ZapfDingbats",
|
34 |
+
"bold": "ZapfDingbats",
|
35 |
+
"italic": "ZapfDingbats",
|
36 |
+
"bold_italic": "ZapfDingbats"
|
37 |
+
},
|
38 |
+
"symbol": {
|
39 |
+
"normal": "Symbol",
|
40 |
+
"bold": "Symbol",
|
41 |
+
"italic": "Symbol",
|
42 |
+
"bold_italic": "Symbol"
|
43 |
+
},
|
44 |
+
"serif": {
|
45 |
+
"normal": "Times-Roman",
|
46 |
+
"bold": "Times-Bold",
|
47 |
+
"italic": "Times-Italic",
|
48 |
+
"bold_italic": "Times-BoldItalic"
|
49 |
+
},
|
50 |
+
"monospace": {
|
51 |
+
"normal": "Courier",
|
52 |
+
"bold": "Courier-Bold",
|
53 |
+
"italic": "Courier-Oblique",
|
54 |
+
"bold_italic": "Courier-BoldOblique"
|
55 |
+
},
|
56 |
+
"fixed": {
|
57 |
+
"normal": "Courier",
|
58 |
+
"bold": "Courier-Bold",
|
59 |
+
"italic": "Courier-Oblique",
|
60 |
+
"bold_italic": "Courier-BoldOblique"
|
61 |
+
},
|
62 |
+
"dejavu sans": {
|
63 |
+
"bold": "DejaVuSans-Bold",
|
64 |
+
"bold_italic": "DejaVuSans-BoldOblique",
|
65 |
+
"italic": "DejaVuSans-Oblique",
|
66 |
+
"normal": "DejaVuSans"
|
67 |
+
},
|
68 |
+
"dejavu sans mono": {
|
69 |
+
"bold": "DejaVuSansMono-Bold",
|
70 |
+
"bold_italic": "DejaVuSansMono-BoldOblique",
|
71 |
+
"italic": "DejaVuSansMono-Oblique",
|
72 |
+
"normal": "DejaVuSansMono"
|
73 |
+
},
|
74 |
+
"dejavu serif": {
|
75 |
+
"bold": "DejaVuSerif-Bold",
|
76 |
+
"bold_italic": "DejaVuSerif-BoldItalic",
|
77 |
+
"italic": "DejaVuSerif-Italic",
|
78 |
+
"normal": "DejaVuSerif"
|
79 |
+
}
|
80 |
+
}
|
vendor/dompdf/dompdf/lib/html5lib/Data.php
DELETED
@@ -1,123 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// warning: this file is encoded in UTF-8!
|
4 |
-
|
5 |
-
class HTML5_Data
|
6 |
-
{
|
7 |
-
|
8 |
-
// at some point this should be moved to a .ser file. Another
|
9 |
-
// possible optimization is to give UTF-8 bytes, not Unicode
|
10 |
-
// codepoints
|
11 |
-
// XXX: Not quite sure why it's named this; this is
|
12 |
-
// actually the numeric entity dereference table.
|
13 |
-
protected static $realCodepointTable = [
|
14 |
-
0x00 => 0xFFFD, // REPLACEMENT CHARACTER
|
15 |
-
0x0D => 0x000A, // LINE FEED (LF)
|
16 |
-
0x80 => 0x20AC, // EURO SIGN ('€')
|
17 |
-
0x81 => 0x0081, // <control>
|
18 |
-
0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK ('‚')
|
19 |
-
0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK ('ƒ')
|
20 |
-
0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK ('„')
|
21 |
-
0x85 => 0x2026, // HORIZONTAL ELLIPSIS ('…')
|
22 |
-
0x86 => 0x2020, // DAGGER ('†')
|
23 |
-
0x87 => 0x2021, // DOUBLE DAGGER ('‡')
|
24 |
-
0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT ('ˆ')
|
25 |
-
0x89 => 0x2030, // PER MILLE SIGN ('‰')
|
26 |
-
0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON ('Š')
|
27 |
-
0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK ('‹')
|
28 |
-
0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE ('Œ')
|
29 |
-
0x8D => 0x008D, // <control>
|
30 |
-
0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON ('Ž')
|
31 |
-
0x8F => 0x008F, // <control>
|
32 |
-
0x90 => 0x0090, // <control>
|
33 |
-
0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK ('‘')
|
34 |
-
0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK ('’')
|
35 |
-
0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK ('“')
|
36 |
-
0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK ('”')
|
37 |
-
0x95 => 0x2022, // BULLET ('•')
|
38 |
-
0x96 => 0x2013, // EN DASH ('–')
|
39 |
-
0x97 => 0x2014, // EM DASH ('—')
|
40 |
-
0x98 => 0x02DC, // SMALL TILDE ('˜')
|
41 |
-
0x99 => 0x2122, // TRADE MARK SIGN ('™')
|
42 |
-
0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON ('š')
|
43 |
-
0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK ('›')
|
44 |
-
0x9C => 0x0153, // LATIN SMALL LIGATURE OE ('œ')
|
45 |
-
0x9D => 0x009D, // <control>
|
46 |
-
0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON ('ž')
|
47 |
-
0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS ('Ÿ')
|
48 |
-
];
|
49 |
-
|
50 |
-
protected static $namedCharacterReferences;
|
51 |
-
|
52 |
-
protected static $namedCharacterReferenceMaxLength;
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Returns the "real" Unicode codepoint of a malformed character
|
56 |
-
* reference.
|
57 |
-
*/
|
58 |
-
public static function getRealCodepoint($ref) {
|
59 |
-
if (!isset(self::$realCodepointTable[$ref])) {
|
60 |
-
return false;
|
61 |
-
} else {
|
62 |
-
return self::$realCodepointTable[$ref];
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
public static function getNamedCharacterReferences() {
|
67 |
-
if (!self::$namedCharacterReferences) {
|
68 |
-
self::$namedCharacterReferences = unserialize(
|
69 |
-
file_get_contents(dirname(__FILE__) . '/named-character-references.ser'));
|
70 |
-
}
|
71 |
-
return self::$namedCharacterReferences;
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* Converts a Unicode codepoint to sequence of UTF-8 bytes.
|
76 |
-
* @note Shamelessly stolen from HTML Purifier, which is also
|
77 |
-
* shamelessly stolen from Feyd (which is in public domain).
|
78 |
-
*/
|
79 |
-
public static function utf8chr($code) {
|
80 |
-
/* We don't care: we live dangerously
|
81 |
-
* if($code > 0x10FFFF or $code < 0x0 or
|
82 |
-
($code >= 0xD800 and $code <= 0xDFFF) ) {
|
83 |
-
// bits are set outside the "valid" range as defined
|
84 |
-
// by UNICODE 4.1.0
|
85 |
-
return "\xEF\xBF\xBD";
|
86 |
-
}*/
|
87 |
-
|
88 |
-
$y = $z = $w = 0;
|
89 |
-
if ($code < 0x80) {
|
90 |
-
// regular ASCII character
|
91 |
-
$x = $code;
|
92 |
-
} else {
|
93 |
-
// set up bits for UTF-8
|
94 |
-
$x = ($code & 0x3F) | 0x80;
|
95 |
-
if ($code < 0x800) {
|
96 |
-
$y = (($code & 0x7FF) >> 6) | 0xC0;
|
97 |
-
} else {
|
98 |
-
$y = (($code & 0xFC0) >> 6) | 0x80;
|
99 |
-
if ($code < 0x10000) {
|
100 |
-
$z = (($code >> 12) & 0x0F) | 0xE0;
|
101 |
-
} else {
|
102 |
-
$z = (($code >> 12) & 0x3F) | 0x80;
|
103 |
-
$w = (($code >> 18) & 0x07) | 0xF0;
|
104 |
-
}
|
105 |
-
}
|
106 |
-
}
|
107 |
-
// set up the actual character
|
108 |
-
$ret = '';
|
109 |
-
if ($w) {
|
110 |
-
$ret .= chr($w);
|
111 |
-
}
|
112 |
-
if ($z) {
|
113 |
-
$ret .= chr($z);
|
114 |
-
}
|
115 |
-
if ($y) {
|
116 |
-
$ret .= chr($y);
|
117 |
-
}
|
118 |
-
$ret .= chr($x);
|
119 |
-
|
120 |
-
return $ret;
|
121 |
-
}
|
122 |
-
|
123 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/lib/html5lib/InputStream.php
DELETED
@@ -1,299 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*
|
4 |
-
|
5 |
-
Copyright 2009 Geoffrey Sneddon <http://gsnedders.com/>
|
6 |
-
|
7 |
-
Permission is hereby granted, free of charge, to any person obtaining a
|
8 |
-
copy of this software and associated documentation files (the
|
9 |
-
"Software"), to deal in the Software without restriction, including
|
10 |
-
without limitation the rights to use, copy, modify, merge, publish,
|
11 |
-
distribute, sublicense, and/or sell copies of the Software, and to
|
12 |
-
permit persons to whom the Software is furnished to do so, subject to
|
13 |
-
the following conditions:
|
14 |
-
|
15 |
-
The above copyright notice and this permission notice shall be included
|
16 |
-
in all copies or substantial portions of the Software.
|
17 |
-
|
18 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
19 |
-
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20 |
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
21 |
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
22 |
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
23 |
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
24 |
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25 |
-
|
26 |
-
*/
|
27 |
-
|
28 |
-
// Some conventions:
|
29 |
-
// /* */ indicates verbatim text from the HTML 5 specification
|
30 |
-
// // indicates regular comments
|
31 |
-
|
32 |
-
class HTML5_InputStream {
|
33 |
-
/**
|
34 |
-
* The string data we're parsing.
|
35 |
-
*/
|
36 |
-
private $data;
|
37 |
-
|
38 |
-
/**
|
39 |
-
* The current integer byte position we are in $data
|
40 |
-
*/
|
41 |
-
private $char;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Length of $data; when $char === $data, we are at the end-of-file.
|
45 |
-
*/
|
46 |
-
private $EOF;
|
47 |
-
|
48 |
-
/**
|
49 |
-
* Parse errors.
|
50 |
-
*/
|
51 |
-
public $errors = [];
|
52 |
-
|
53 |
-
/**
|
54 |
-
* @param $data | Data to parse
|
55 |
-
* @throws Exception
|
56 |
-
*/
|
57 |
-
public function __construct($data) {
|
58 |
-
|
59 |
-
/* Given an encoding, the bytes in the input stream must be
|
60 |
-
converted to Unicode characters for the tokeniser, as
|
61 |
-
described by the rules for that encoding, except that the
|
62 |
-
leading U+FEFF BYTE ORDER MARK character, if any, must not
|
63 |
-
be stripped by the encoding layer (it is stripped by the rule below).
|
64 |
-
|
65 |
-
Bytes or sequences of bytes in the original byte stream that
|
66 |
-
could not be converted to Unicode characters must be converted
|
67 |
-
to U+FFFD REPLACEMENT CHARACTER code points. */
|
68 |
-
|
69 |
-
// XXX currently assuming input data is UTF-8; once we
|
70 |
-
// build encoding detection this will no longer be the case
|
71 |
-
//
|
72 |
-
// We previously had an mbstring implementation here, but that
|
73 |
-
// implementation is heavily non-conforming, so it's been
|
74 |
-
// omitted.
|
75 |
-
if (extension_loaded('iconv')) {
|
76 |
-
// non-conforming
|
77 |
-
$data = @iconv('UTF-8', 'UTF-8//IGNORE', $data);
|
78 |
-
} else {
|
79 |
-
// we can make a conforming native implementation
|
80 |
-
throw new Exception('Not implemented, please install iconv');
|
81 |
-
}
|
82 |
-
|
83 |
-
/* One leading U+FEFF BYTE ORDER MARK character must be
|
84 |
-
ignored if any are present. */
|
85 |
-
if (substr($data, 0, 3) === "\xEF\xBB\xBF") {
|
86 |
-
$data = substr($data, 3);
|
87 |
-
}
|
88 |
-
|
89 |
-
/* All U+0000 NULL characters in the input must be replaced
|
90 |
-
by U+FFFD REPLACEMENT CHARACTERs. Any occurrences of such
|
91 |
-
characters is a parse error. */
|
92 |
-
for ($i = 0, $count = substr_count($data, "\0"); $i < $count; $i++) {
|
93 |
-
$this->errors[] = [
|
94 |
-
'type' => HTML5_Tokenizer::PARSEERROR,
|
95 |
-
'data' => 'null-character'
|
96 |
-
];
|
97 |
-
}
|
98 |
-
/* U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED
|
99 |
-
(LF) characters are treated specially. Any CR characters
|
100 |
-
that are followed by LF characters must be removed, and any
|
101 |
-
CR characters not followed by LF characters must be converted
|
102 |
-
to LF characters. Thus, newlines in HTML DOMs are represented
|
103 |
-
by LF characters, and there are never any CR characters in the
|
104 |
-
input to the tokenization stage. */
|
105 |
-
$data = str_replace(
|
106 |
-
[
|
107 |
-
"\0",
|
108 |
-
"\r\n",
|
109 |
-
"\r"
|
110 |
-
],
|
111 |
-
[
|
112 |
-
"\xEF\xBF\xBD",
|
113 |
-
"\n",
|
114 |
-
"\n"
|
115 |
-
],
|
116 |
-
$data
|
117 |
-
);
|
118 |
-
|
119 |
-
/* Any occurrences of any characters in the ranges U+0001 to
|
120 |
-
U+0008, U+000B, U+000E to U+001F, U+007F to U+009F,
|
121 |
-
U+D800 to U+DFFF , U+FDD0 to U+FDEF, and
|
122 |
-
characters U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, U+2FFFE, U+2FFFF,
|
123 |
-
U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE,
|
124 |
-
U+6FFFF, U+7FFFE, U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF,
|
125 |
-
U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF, U+DFFFE,
|
126 |
-
U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, and
|
127 |
-
U+10FFFF are parse errors. (These are all control characters
|
128 |
-
or permanently undefined Unicode characters.) */
|
129 |
-
// Check PCRE is loaded.
|
130 |
-
if (extension_loaded('pcre')) {
|
131 |
-
$count = preg_match_all(
|
132 |
-
'/(?:
|
133 |
-
[\x01-\x08\x0B\x0E-\x1F\x7F] # U+0001 to U+0008, U+000B, U+000E to U+001F and U+007F
|
134 |
-
|
|
135 |
-
\xC2[\x80-\x9F] # U+0080 to U+009F
|
136 |
-
|
|
137 |
-
\xED(?:\xA0[\x80-\xFF]|[\xA1-\xBE][\x00-\xFF]|\xBF[\x00-\xBF]) # U+D800 to U+DFFFF
|
138 |
-
|
|
139 |
-
\xEF\xB7[\x90-\xAF] # U+FDD0 to U+FDEF
|
140 |
-
|
|
141 |
-
\xEF\xBF[\xBE\xBF] # U+FFFE and U+FFFF
|
142 |
-
|
|
143 |
-
[\xF0-\xF4][\x8F-\xBF]\xBF[\xBE\xBF] # U+nFFFE and U+nFFFF (1 <= n <= 10_{16})
|
144 |
-
)/x',
|
145 |
-
$data,
|
146 |
-
$matches
|
147 |
-
);
|
148 |
-
for ($i = 0; $i < $count; $i++) {
|
149 |
-
$this->errors[] = [
|
150 |
-
'type' => HTML5_Tokenizer::PARSEERROR,
|
151 |
-
'data' => 'invalid-codepoint'
|
152 |
-
];
|
153 |
-
}
|
154 |
-
} else {
|
155 |
-
// XXX: Need non-PCRE impl, probably using substr_count
|
156 |
-
}
|
157 |
-
|
158 |
-
$this->data = $data;
|
159 |
-
$this->char = 0;
|
160 |
-
$this->EOF = strlen($data);
|
161 |
-
}
|
162 |
-
|
163 |
-
/**
|
164 |
-
* Returns the current line that the tokenizer is at.
|
165 |
-
*
|
166 |
-
* @return int
|
167 |
-
*/
|
168 |
-
public function getCurrentLine() {
|
169 |
-
// Check the string isn't empty
|
170 |
-
if ($this->EOF) {
|
171 |
-
// Add one to $this->char because we want the number for the next
|
172 |
-
// byte to be processed.
|
173 |
-
return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1;
|
174 |
-
} else {
|
175 |
-
// If the string is empty, we are on the first line (sorta).
|
176 |
-
return 1;
|
177 |
-
}
|
178 |
-
}
|
179 |
-
|
180 |
-
/**
|
181 |
-
* Returns the current column of the current line that the tokenizer is at.
|
182 |
-
*
|
183 |
-
* @return int
|
184 |
-
*/
|
185 |
-
public function getColumnOffset() {
|
186 |
-
// strrpos is weird, and the offset needs to be negative for what we
|
187 |
-
// want (i.e., the last \n before $this->char). This needs to not have
|
188 |
-
// one (to make it point to the next character, the one we want the
|
189 |
-
// position of) added to it because strrpos's behaviour includes the
|
190 |
-
// final offset byte.
|
191 |
-
$lastLine = strrpos($this->data, "\n", $this->char - 1 - strlen($this->data));
|
192 |
-
|
193 |
-
// However, for here we want the length up until the next byte to be
|
194 |
-
// processed, so add one to the current byte ($this->char).
|
195 |
-
if ($lastLine !== false) {
|
196 |
-
$findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine);
|
197 |
-
} else {
|
198 |
-
$findLengthOf = substr($this->data, 0, $this->char);
|
199 |
-
}
|
200 |
-
|
201 |
-
// Get the length for the string we need.
|
202 |
-
if (extension_loaded('iconv')) {
|
203 |
-
return iconv_strlen($findLengthOf, 'utf-8');
|
204 |
-
} elseif (extension_loaded('mbstring')) {
|
205 |
-
return mb_strlen($findLengthOf, 'utf-8');
|
206 |
-
} elseif (extension_loaded('xml')) {
|
207 |
-
return strlen(utf8_decode($findLengthOf));
|
208 |
-
} else {
|
209 |
-
$count = count_chars($findLengthOf);
|
210 |
-
// 0x80 = 0x7F - 0 + 1 (one added to get inclusive range)
|
211 |
-
// 0x33 = 0xF4 - 0x2C + 1 (one added to get inclusive range)
|
212 |
-
return array_sum(array_slice($count, 0, 0x80)) +
|
213 |
-
array_sum(array_slice($count, 0xC2, 0x33));
|
214 |
-
}
|
215 |
-
}
|
216 |
-
|
217 |
-
/**
|
218 |
-
* Retrieve the currently consume character.
|
219 |
-
* @note This performs bounds checking
|
220 |
-
*
|
221 |
-
* @return bool|string
|
222 |
-
*/
|
223 |
-
public function char() {
|
224 |
-
return ($this->char++ < $this->EOF)
|
225 |
-
? $this->data[$this->char - 1]
|
226 |
-
: false;
|
227 |
-
}
|
228 |
-
|
229 |
-
/**
|
230 |
-
* Get all characters until EOF.
|
231 |
-
* @note This performs bounds checking
|
232 |
-
*
|
233 |
-
* @return string|bool
|
234 |
-
*/
|
235 |
-
public function remainingChars() {
|
236 |
-
if ($this->char < $this->EOF) {
|
237 |
-
$data = substr($this->data, $this->char);
|
238 |
-
$this->char = $this->EOF;
|
239 |
-
return $data;
|
240 |
-
} else {
|
241 |
-
return false;
|
242 |
-
}
|
243 |
-
}
|
244 |
-
|
245 |
-
/**
|
246 |
-
* Matches as far as possible until we reach a certain set of bytes
|
247 |
-
* and returns the matched substring.
|
248 |
-
*
|
249 |
-
* @param $bytes | Bytes to match.
|
250 |
-
* @param null $max
|
251 |
-
* @return bool|string
|
252 |
-
*/
|
253 |
-
public function charsUntil($bytes, $max = null) {
|
254 |
-
if ($this->char < $this->EOF) {
|
255 |
-
if ($max === 0 || $max) {
|
256 |
-
$len = strcspn($this->data, $bytes, $this->char, $max);
|
257 |
-
} else {
|
258 |
-
$len = strcspn($this->data, $bytes, $this->char);
|
259 |
-
}
|
260 |
-
$string = (string) substr($this->data, $this->char, $len);
|
261 |
-
$this->char += $len;
|
262 |
-
return $string;
|
263 |
-
} else {
|
264 |
-
return false;
|
265 |
-
}
|
266 |
-
}
|
267 |
-
|
268 |
-
/**
|
269 |
-
* Matches as far as possible with a certain set of bytes
|
270 |
-
* and returns the matched substring.
|
271 |
-
*
|
272 |
-
* @param $bytes | Bytes to match.
|
273 |
-
* @param null $max
|
274 |
-
* @return bool|string
|
275 |
-
*/
|
276 |
-
public function charsWhile($bytes, $max = null) {
|
277 |
-
if ($this->char < $this->EOF) {
|
278 |
-
if ($max === 0 || $max) {
|
279 |
-
$len = strspn($this->data, $bytes, $this->char, $max);
|
280 |
-
} else {
|
281 |
-
$len = strspn($this->data, $bytes, $this->char);
|
282 |
-
}
|
283 |
-
$string = (string) substr($this->data, $this->char, $len);
|
284 |
-
$this->char += $len;
|
285 |
-
return $string;
|
286 |
-
} else {
|
287 |
-
return false;
|
288 |
-
}
|
289 |
-
}
|
290 |
-
|
291 |
-
/**
|
292 |
-
* Unconsume one character.
|
293 |
-
*/
|
294 |
-
public function unget() {
|
295 |
-
if ($this->char <= $this->EOF) {
|
296 |
-
$this->char--;
|
297 |
-
}
|
298 |
-
}
|
299 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/lib/html5lib/Parser.php
DELETED
@@ -1,37 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
require_once dirname(__FILE__) . '/Data.php';
|
4 |
-
require_once dirname(__FILE__) . '/InputStream.php';
|
5 |
-
require_once dirname(__FILE__) . '/TreeBuilder.php';
|
6 |
-
require_once dirname(__FILE__) . '/Tokenizer.php';
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Outwards facing interface for HTML5.
|
10 |
-
*/
|
11 |
-
class HTML5_Parser
|
12 |
-
{
|
13 |
-
/**
|
14 |
-
* Parses a full HTML document.
|
15 |
-
* @param $text | HTML text to parse
|
16 |
-
* @param $builder | Custom builder implementation
|
17 |
-
* @return DOMDocument|DOMNodeList Parsed HTML as DOMDocument
|
18 |
-
*/
|
19 |
-
public static function parse($text, $builder = null) {
|
20 |
-
$tokenizer = new HTML5_Tokenizer($text, $builder);
|
21 |
-
$tokenizer->parse();
|
22 |
-
return $tokenizer->save();
|
23 |
-
}
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Parses an HTML fragment.
|
27 |
-
* @param $text | HTML text to parse
|
28 |
-
* @param $context String name of context element to pretend parsing is in.
|
29 |
-
* @param $builder | Custom builder implementation
|
30 |
-
* @return DOMDocument|DOMNodeList Parsed HTML as DOMDocument
|
31 |
-
*/
|
32 |
-
public static function parseFragment($text, $context = null, $builder = null) {
|
33 |
-
$tokenizer = new HTML5_Tokenizer($text, $builder);
|
34 |
-
$tokenizer->parseFragment($context);
|
35 |
-
return $tokenizer->save();
|
36 |
-
}
|
37 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/lib/html5lib/Tokenizer.php
DELETED
@@ -1,2470 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*
|
4 |
-
|
5 |
-
Copyright 2007 Jeroen van der Meer <http://jero.net/>
|
6 |
-
Copyright 2008 Edward Z. Yang <http://htmlpurifier.org/>
|
7 |
-
Copyright 2009 Geoffrey Sneddon <http://gsnedders.com/>
|
8 |
-
|
9 |
-
Permission is hereby granted, free of charge, to any person obtaining a
|
10 |
-
copy of this software and associated documentation files (the
|
11 |
-
"Software"), to deal in the Software without restriction, including
|
12 |
-
without limitation the rights to use, copy, modify, merge, publish,
|
13 |
-
distribute, sublicense, and/or sell copies of the Software, and to
|
14 |
-
permit persons to whom the Software is furnished to do so, subject to
|
15 |
-
the following conditions:
|
16 |
-
|
17 |
-
The above copyright notice and this permission notice shall be included
|
18 |
-
in all copies or substantial portions of the Software.
|
19 |
-
|
20 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
21 |
-
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22 |
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
23 |
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
24 |
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
25 |
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
26 |
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27 |
-
|
28 |
-
*/
|
29 |
-
|
30 |
-
// Some conventions:
|
31 |
-
// /* */ indicates verbatim text from the HTML 5 specification
|
32 |
-
// // indicates regular comments
|
33 |
-
|
34 |
-
// all flags are in hyphenated form
|
35 |
-
|
36 |
-
class HTML5_Tokenizer {
|
37 |
-
/**
|
38 |
-
* @var HTML5_InputStream
|
39 |
-
*
|
40 |
-
* Points to an InputStream object.
|
41 |
-
*/
|
42 |
-
protected $stream;
|
43 |
-
|
44 |
-
/**
|
45 |
-
* @var HTML5_TreeBuilder
|
46 |
-
*
|
47 |
-
* Tree builder that the tokenizer emits token to.
|
48 |
-
*/
|
49 |
-
private $tree;
|
50 |
-
|
51 |
-
/**
|
52 |
-
* @var int
|
53 |
-
*
|
54 |
-
* Current content model we are parsing as.
|
55 |
-
*/
|
56 |
-
protected $content_model;
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Current token that is being built, but not yet emitted. Also
|
60 |
-
* is the last token emitted, if applicable.
|
61 |
-
*/
|
62 |
-
protected $token;
|
63 |
-
|
64 |
-
// These are constants describing the content model
|
65 |
-
const PCDATA = 0;
|
66 |
-
const RCDATA = 1;
|
67 |
-
const CDATA = 2;
|
68 |
-
const PLAINTEXT = 3;
|
69 |
-
|
70 |
-
// These are constants describing tokens
|
71 |
-
// XXX should probably be moved somewhere else, probably the
|
72 |
-
// HTML5 class.
|
73 |
-
const DOCTYPE = 0;
|
74 |
-
const STARTTAG = 1;
|
75 |
-
const ENDTAG = 2;
|
76 |
-
const COMMENT = 3;
|
77 |
-
const CHARACTER = 4;
|
78 |
-
const SPACECHARACTER = 5;
|
79 |
-
const EOF = 6;
|
80 |
-
const PARSEERROR = 7;
|
81 |
-
|
82 |
-
// These are constants representing bunches of characters.
|
83 |
-
const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
84 |
-
const UPPER_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
85 |
-
const LOWER_ALPHA = 'abcdefghijklmnopqrstuvwxyz';
|
86 |
-
const DIGIT = '0123456789';
|
87 |
-
const HEX = '0123456789ABCDEFabcdef';
|
88 |
-
const WHITESPACE = "\t\n\x0c ";
|
89 |
-
|
90 |
-
/**
|
91 |
-
* @param $data | Data to parse
|
92 |
-
* @param HTML5_TreeBuilder|null $builder
|
93 |
-
*/
|
94 |
-
public function __construct($data, $builder = null) {
|
95 |
-
$this->stream = new HTML5_InputStream($data);
|
96 |
-
if (!$builder) {
|
97 |
-
$this->tree = new HTML5_TreeBuilder;
|
98 |
-
} else {
|
99 |
-
$this->tree = $builder;
|
100 |
-
}
|
101 |
-
$this->content_model = self::PCDATA;
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* @param null $context
|
106 |
-
*/
|
107 |
-
public function parseFragment($context = null) {
|
108 |
-
$this->tree->setupContext($context);
|
109 |
-
if ($this->tree->content_model) {
|
110 |
-
$this->content_model = $this->tree->content_model;
|
111 |
-
$this->tree->content_model = null;
|
112 |
-
}
|
113 |
-
$this->parse();
|
114 |
-
}
|
115 |
-
|
116 |
-
// XXX maybe convert this into an iterator? regardless, this function
|
117 |
-
// and the save function should go into a Parser facade of some sort
|
118 |
-
/**
|
119 |
-
* Performs the actual parsing of the document.
|
120 |
-
*/
|
121 |
-
public function parse() {
|
122 |
-
// Current state
|
123 |
-
$state = 'data';
|
124 |
-
// This is used to avoid having to have look-behind in the data state.
|
125 |
-
$lastFourChars = '';
|
126 |
-
/**
|
127 |
-
* Escape flag as specified by the HTML5 specification: "used to
|
128 |
-
* control the behavior of the tokeniser. It is either true or
|
129 |
-
* false, and initially must be set to the false state."
|
130 |
-
*/
|
131 |
-
$escape = false;
|
132 |
-
//echo "\n\n";
|
133 |
-
while($state !== null) {
|
134 |
-
|
135 |
-
/*echo $state . ' ';
|
136 |
-
switch ($this->content_model) {
|
137 |
-
case self::PCDATA: echo 'PCDATA'; break;
|
138 |
-
case self::RCDATA: echo 'RCDATA'; break;
|
139 |
-
case self::CDATA: echo 'CDATA'; break;
|
140 |
-
case self::PLAINTEXT: echo 'PLAINTEXT'; break;
|
141 |
-
}
|
142 |
-
if ($escape) echo " escape";
|
143 |
-
echo "\n";*/
|
144 |
-
|
145 |
-
switch($state) {
|
146 |
-
case 'data':
|
147 |
-
|
148 |
-
/* Consume the next input character */
|
149 |
-
$char = $this->stream->char();
|
150 |
-
$lastFourChars .= $char;
|
151 |
-
if (strlen($lastFourChars) > 4) {
|
152 |
-
$lastFourChars = substr($lastFourChars, -4);
|
153 |
-
}
|
154 |
-
|
155 |
-
// see below for meaning
|
156 |
-
$hyp_cond =
|
157 |
-
!$escape &&
|
158 |
-
(
|
159 |
-
$this->content_model === self::RCDATA ||
|
160 |
-
$this->content_model === self::CDATA
|
161 |
-
);
|
162 |
-
$amp_cond =
|
163 |
-
!$escape &&
|
164 |
-
(
|
165 |
-
$this->content_model === self::PCDATA ||
|
166 |
-
$this->content_model === self::RCDATA
|
167 |
-
);
|
168 |
-
$lt_cond =
|
169 |
-
$this->content_model === self::PCDATA ||
|
170 |
-
(
|
171 |
-
(
|
172 |
-
$this->content_model === self::RCDATA ||
|
173 |
-
$this->content_model === self::CDATA
|
174 |
-
) &&
|
175 |
-
!$escape
|
176 |
-
);
|
177 |
-
$gt_cond =
|
178 |
-
$escape &&
|
179 |
-
(
|
180 |
-
$this->content_model === self::RCDATA ||
|
181 |
-
$this->content_model === self::CDATA
|
182 |
-
);
|
183 |
-
|
184 |
-
if ($char === '&' && $amp_cond === true) {
|
185 |
-
/* U+0026 AMPERSAND (&)
|
186 |
-
When the content model flag is set to one of the PCDATA or RCDATA
|
187 |
-
states and the escape flag is false: switch to the
|
188 |
-
character reference data state. Otherwise: treat it as per
|
189 |
-
the "anything else" entry below. */
|
190 |
-
$state = 'character reference data';
|
191 |
-
|
192 |
-
} elseif (
|
193 |
-
$char === '-' &&
|
194 |
-
$hyp_cond === true &&
|
195 |
-
$lastFourChars === '<!--'
|
196 |
-
) {
|
197 |
-
/*
|
198 |
-
U+002D HYPHEN-MINUS (-)
|
199 |
-
If the content model flag is set to either the RCDATA state or
|
200 |
-
the CDATA state, and the escape flag is false, and there are at
|
201 |
-
least three characters before this one in the input stream, and the
|
202 |
-
last four characters in the input stream, including this one, are
|
203 |
-
U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS,
|
204 |
-
and U+002D HYPHEN-MINUS ("<!--"), then set the escape flag to true. */
|
205 |
-
$escape = true;
|
206 |
-
|
207 |
-
/* In any case, emit the input character as a character token. Stay
|
208 |
-
in the data state. */
|
209 |
-
$this->emitToken([
|
210 |
-
'type' => self::CHARACTER,
|
211 |
-
'data' => '-'
|
212 |
-
]);
|
213 |
-
// We do the "any case" part as part of "anything else".
|
214 |
-
|
215 |
-
/* U+003C LESS-THAN SIGN (<) */
|
216 |
-
} elseif ($char === '<' && $lt_cond === true) {
|
217 |
-
/* When the content model flag is set to the PCDATA state: switch
|
218 |
-
to the tag open state.
|
219 |
-
|
220 |
-
When the content model flag is set to either the RCDATA state or
|
221 |
-
the CDATA state and the escape flag is false: switch to the tag
|
222 |
-
open state.
|
223 |
-
|
224 |
-
Otherwise: treat it as per the "anything else" entry below. */
|
225 |
-
$state = 'tag open';
|
226 |
-
|
227 |
-
/* U+003E GREATER-THAN SIGN (>) */
|
228 |
-
} elseif (
|
229 |
-
$char === '>' &&
|
230 |
-
$gt_cond === true &&
|
231 |
-
substr($lastFourChars, 1) === '-->'
|
232 |
-
) {
|
233 |
-
/* If the content model flag is set to either the RCDATA state or
|
234 |
-
the CDATA state, and the escape flag is true, and the last three
|
235 |
-
characters in the input stream including this one are U+002D
|
236 |
-
HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"),
|
237 |
-
set the escape flag to false. */
|
238 |
-
$escape = false;
|
239 |
-
|
240 |
-
/* In any case, emit the input character as a character token.
|
241 |
-
Stay in the data state. */
|
242 |
-
$this->emitToken([
|
243 |
-
'type' => self::CHARACTER,
|
244 |
-
'data' => '>'
|
245 |
-
]);
|
246 |
-
// We do the "any case" part as part of "anything else".
|
247 |
-
|
248 |
-
} elseif ($char === false) {
|
249 |
-
/* EOF
|
250 |
-
Emit an end-of-file token. */
|
251 |
-
$state = null;
|
252 |
-
$this->tree->emitToken([
|
253 |
-
'type' => self::EOF
|
254 |
-
]);
|
255 |
-
|
256 |
-
} elseif ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
257 |
-
// Directly after emitting a token you switch back to the "data
|
258 |
-
// state". At that point spaceCharacters are important so they are
|
259 |
-
// emitted separately.
|
260 |
-
$chars = $this->stream->charsWhile(self::WHITESPACE);
|
261 |
-
$this->emitToken([
|
262 |
-
'type' => self::SPACECHARACTER,
|
263 |
-
'data' => $char . $chars
|
264 |
-
]);
|
265 |
-
$lastFourChars .= $chars;
|
266 |
-
if (strlen($lastFourChars) > 4) {
|
267 |
-
$lastFourChars = substr($lastFourChars, -4);
|
268 |
-
}
|
269 |
-
} else {
|
270 |
-
/* Anything else
|
271 |
-
THIS IS AN OPTIMIZATION: Get as many character that
|
272 |
-
otherwise would also be treated as a character token and emit it
|
273 |
-
as a single character token. Stay in the data state. */
|
274 |
-
|
275 |
-
$mask = '';
|
276 |
-
if ($hyp_cond === true) {
|
277 |
-
$mask .= '-';
|
278 |
-
}
|
279 |
-
if ($amp_cond === true) {
|
280 |
-
$mask .= '&';
|
281 |
-
}
|
282 |
-
if ($lt_cond === true) {
|
283 |
-
$mask .= '<';
|
284 |
-
}
|
285 |
-
if ($gt_cond === true) {
|
286 |
-
$mask .= '>';
|
287 |
-
}
|
288 |
-
|
289 |
-
if ($mask === '') {
|
290 |
-
$chars = $this->stream->remainingChars();
|
291 |
-
} else {
|
292 |
-
$chars = $this->stream->charsUntil($mask);
|
293 |
-
}
|
294 |
-
|
295 |
-
$this->emitToken([
|
296 |
-
'type' => self::CHARACTER,
|
297 |
-
'data' => $char . $chars
|
298 |
-
]);
|
299 |
-
|
300 |
-
$lastFourChars .= $chars;
|
301 |
-
if (strlen($lastFourChars) > 4) {
|
302 |
-
$lastFourChars = substr($lastFourChars, -4);
|
303 |
-
}
|
304 |
-
|
305 |
-
$state = 'data';
|
306 |
-
}
|
307 |
-
break;
|
308 |
-
|
309 |
-
case 'character reference data':
|
310 |
-
/* (This cannot happen if the content model flag
|
311 |
-
is set to the CDATA state.) */
|
312 |
-
|
313 |
-
/* Attempt to consume a character reference, with no
|
314 |
-
additional allowed character. */
|
315 |
-
$entity = $this->consumeCharacterReference();
|
316 |
-
|
317 |
-
/* If nothing is returned, emit a U+0026 AMPERSAND
|
318 |
-
character token. Otherwise, emit the character token that
|
319 |
-
was returned. */
|
320 |
-
// This is all done when consuming the character reference.
|
321 |
-
$this->emitToken([
|
322 |
-
'type' => self::CHARACTER,
|
323 |
-
'data' => $entity
|
324 |
-
]);
|
325 |
-
|
326 |
-
/* Finally, switch to the data state. */
|
327 |
-
$state = 'data';
|
328 |
-
break;
|
329 |
-
|
330 |
-
case 'tag open':
|
331 |
-
$char = $this->stream->char();
|
332 |
-
|
333 |
-
switch ($this->content_model) {
|
334 |
-
case self::RCDATA:
|
335 |
-
case self::CDATA:
|
336 |
-
/* Consume the next input character. If it is a
|
337 |
-
U+002F SOLIDUS (/) character, switch to the close
|
338 |
-
tag open state. Otherwise, emit a U+003C LESS-THAN
|
339 |
-
SIGN character token and reconsume the current input
|
340 |
-
character in the data state. */
|
341 |
-
// We consumed above.
|
342 |
-
|
343 |
-
if ($char === '/') {
|
344 |
-
$state = 'close tag open';
|
345 |
-
} else {
|
346 |
-
$this->emitToken([
|
347 |
-
'type' => self::CHARACTER,
|
348 |
-
'data' => '<'
|
349 |
-
]);
|
350 |
-
|
351 |
-
$this->stream->unget();
|
352 |
-
|
353 |
-
$state = 'data';
|
354 |
-
}
|
355 |
-
break;
|
356 |
-
|
357 |
-
case self::PCDATA:
|
358 |
-
/* If the content model flag is set to the PCDATA state
|
359 |
-
Consume the next input character: */
|
360 |
-
// We consumed above.
|
361 |
-
|
362 |
-
if ($char === '!') {
|
363 |
-
/* U+0021 EXCLAMATION MARK (!)
|
364 |
-
Switch to the markup declaration open state. */
|
365 |
-
$state = 'markup declaration open';
|
366 |
-
|
367 |
-
} elseif ($char === '/') {
|
368 |
-
/* U+002F SOLIDUS (/)
|
369 |
-
Switch to the close tag open state. */
|
370 |
-
$state = 'close tag open';
|
371 |
-
|
372 |
-
} elseif ('A' <= $char && $char <= 'Z') {
|
373 |
-
/* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
|
374 |
-
Create a new start tag token, set its tag name to the lowercase
|
375 |
-
version of the input character (add 0x0020 to the character's code
|
376 |
-
point), then switch to the tag name state. (Don't emit the token
|
377 |
-
yet; further details will be filled in before it is emitted.) */
|
378 |
-
$this->token = [
|
379 |
-
'name' => strtolower($char),
|
380 |
-
'type' => self::STARTTAG,
|
381 |
-
'attr' => []
|
382 |
-
];
|
383 |
-
|
384 |
-
$state = 'tag name';
|
385 |
-
|
386 |
-
} elseif ('a' <= $char && $char <= 'z') {
|
387 |
-
/* U+0061 LATIN SMALL LETTER A through to U+007A LATIN SMALL LETTER Z
|
388 |
-
Create a new start tag token, set its tag name to the input
|
389 |
-
character, then switch to the tag name state. (Don't emit
|
390 |
-
the token yet; further details will be filled in before it
|
391 |
-
is emitted.) */
|
392 |
-
$this->token = [
|
393 |
-
'name' => $char,
|
394 |
-
'type' => self::STARTTAG,
|
395 |
-
'attr' => []
|
396 |
-
];
|
397 |
-
|
398 |
-
$state = 'tag name';
|
399 |
-
|
400 |
-
} elseif ($char === '>') {
|
401 |
-
/* U+003E GREATER-THAN SIGN (>)
|
402 |
-
Parse error. Emit a U+003C LESS-THAN SIGN character token and a
|
403 |
-
U+003E GREATER-THAN SIGN character token. Switch to the data state. */
|
404 |
-
$this->emitToken([
|
405 |
-
'type' => self::PARSEERROR,
|
406 |
-
'data' => 'expected-tag-name-but-got-right-bracket'
|
407 |
-
]);
|
408 |
-
$this->emitToken([
|
409 |
-
'type' => self::CHARACTER,
|
410 |
-
'data' => '<>'
|
411 |
-
]);
|
412 |
-
|
413 |
-
$state = 'data';
|
414 |
-
|
415 |
-
} elseif ($char === '?') {
|
416 |
-
/* U+003F QUESTION MARK (?)
|
417 |
-
Parse error. Switch to the bogus comment state. */
|
418 |
-
$this->emitToken([
|
419 |
-
'type' => self::PARSEERROR,
|
420 |
-
'data' => 'expected-tag-name-but-got-question-mark'
|
421 |
-
]);
|
422 |
-
$this->token = [
|
423 |
-
'data' => '?',
|
424 |
-
'type' => self::COMMENT
|
425 |
-
];
|
426 |
-
$state = 'bogus comment';
|
427 |
-
|
428 |
-
} else {
|
429 |
-
/* Anything else
|
430 |
-
Parse error. Emit a U+003C LESS-THAN SIGN character token and
|
431 |
-
reconsume the current input character in the data state. */
|
432 |
-
$this->emitToken([
|
433 |
-
'type' => self::PARSEERROR,
|
434 |
-
'data' => 'expected-tag-name'
|
435 |
-
]);
|
436 |
-
$this->emitToken([
|
437 |
-
'type' => self::CHARACTER,
|
438 |
-
'data' => '<'
|
439 |
-
]);
|
440 |
-
|
441 |
-
$state = 'data';
|
442 |
-
$this->stream->unget();
|
443 |
-
}
|
444 |
-
break;
|
445 |
-
}
|
446 |
-
break;
|
447 |
-
|
448 |
-
case 'close tag open':
|
449 |
-
if (
|
450 |
-
$this->content_model === self::RCDATA ||
|
451 |
-
$this->content_model === self::CDATA
|
452 |
-
) {
|
453 |
-
/* If the content model flag is set to the RCDATA or CDATA
|
454 |
-
states... */
|
455 |
-
$name = strtolower($this->stream->charsWhile(self::ALPHA));
|
456 |
-
$following = $this->stream->char();
|
457 |
-
$this->stream->unget();
|
458 |
-
if (
|
459 |
-
!$this->token ||
|
460 |
-
$this->token['name'] !== $name ||
|
461 |
-
$this->token['name'] === $name && !in_array($following, ["\x09", "\x0A", "\x0C", "\x20", "\x3E", "\x2F", false])
|
462 |
-
) {
|
463 |
-
/* if no start tag token has ever been emitted by this instance
|
464 |
-
of the tokenizer (fragment case), or, if the next few
|
465 |
-
characters do not match the tag name of the last start tag
|
466 |
-
token emitted (compared in an ASCII case-insensitive manner),
|
467 |
-
or if they do but they are not immediately followed by one of
|
468 |
-
the following characters:
|
469 |
-
|
470 |
-
* U+0009 CHARACTER TABULATION
|
471 |
-
* U+000A LINE FEED (LF)
|
472 |
-
* U+000C FORM FEED (FF)
|
473 |
-
* U+0020 SPACE
|
474 |
-
* U+003E GREATER-THAN SIGN (>)
|
475 |
-
* U+002F SOLIDUS (/)
|
476 |
-
* EOF
|
477 |
-
|
478 |
-
...then emit a U+003C LESS-THAN SIGN character token, a
|
479 |
-
U+002F SOLIDUS character token, and switch to the data
|
480 |
-
state to process the next input character. */
|
481 |
-
// XXX: Probably ought to replace in_array with $following === x ||...
|
482 |
-
|
483 |
-
// We also need to emit $name now we've consumed that, as we
|
484 |
-
// know it'll just be emitted as a character token.
|
485 |
-
$this->emitToken([
|
486 |
-
'type' => self::CHARACTER,
|
487 |
-
'data' => '</' . $name
|
488 |
-
]);
|
489 |
-
|
490 |
-
$state = 'data';
|
491 |
-
} else {
|
492 |
-
// This matches what would happen if we actually did the
|
493 |
-
// otherwise below (but we can't because we've consumed too
|
494 |
-
// much).
|
495 |
-
|
496 |
-
// Start the end tag token with the name we already have.
|
497 |
-
$this->token = [
|
498 |
-
'name' => $name,
|
499 |
-
'type' => self::ENDTAG
|
500 |
-
];
|
501 |
-
|
502 |
-
// Change to tag name state.
|
503 |
-
$state = 'tag name';
|
504 |
-
}
|
505 |
-
} elseif ($this->content_model === self::PCDATA) {
|
506 |
-
/* Otherwise, if the content model flag is set to the PCDATA
|
507 |
-
state [...]: */
|
508 |
-
$char = $this->stream->char();
|
509 |
-
|
510 |
-
if ('A' <= $char && $char <= 'Z') {
|
511 |
-
/* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
|
512 |
-
Create a new end tag token, set its tag name to the lowercase version
|
513 |
-
of the input character (add 0x0020 to the character's code point), then
|
514 |
-
switch to the tag name state. (Don't emit the token yet; further details
|
515 |
-
will be filled in before it is emitted.) */
|
516 |
-
$this->token = [
|
517 |
-
'name' => strtolower($char),
|
518 |
-
'type' => self::ENDTAG
|
519 |
-
];
|
520 |
-
|
521 |
-
$state = 'tag name';
|
522 |
-
|
523 |
-
} elseif ('a' <= $char && $char <= 'z') {
|
524 |
-
/* U+0061 LATIN SMALL LETTER A through to U+007A LATIN SMALL LETTER Z
|
525 |
-
Create a new end tag token, set its tag name to the
|
526 |
-
input character, then switch to the tag name state.
|
527 |
-
(Don't emit the token yet; further details will be
|
528 |
-
filled in before it is emitted.) */
|
529 |
-
$this->token = [
|
530 |
-
'name' => $char,
|
531 |
-
'type' => self::ENDTAG
|
532 |
-
];
|
533 |
-
|
534 |
-
$state = 'tag name';
|
535 |
-
|
536 |
-
} elseif ($char === '>') {
|
537 |
-
/* U+003E GREATER-THAN SIGN (>)
|
538 |
-
Parse error. Switch to the data state. */
|
539 |
-
$this->emitToken([
|
540 |
-
'type' => self::PARSEERROR,
|
541 |
-
'data' => 'expected-closing-tag-but-got-right-bracket'
|
542 |
-
]);
|
543 |
-
$state = 'data';
|
544 |
-
|
545 |
-
} elseif ($char === false) {
|
546 |
-
/* EOF
|
547 |
-
Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F
|
548 |
-
SOLIDUS character token. Reconsume the EOF character in the data state. */
|
549 |
-
$this->emitToken([
|
550 |
-
'type' => self::PARSEERROR,
|
551 |
-
'data' => 'expected-closing-tag-but-got-eof'
|
552 |
-
]);
|
553 |
-
$this->emitToken([
|
554 |
-
'type' => self::CHARACTER,
|
555 |
-
'data' => '</'
|
556 |
-
]);
|
557 |
-
|
558 |
-
$this->stream->unget();
|
559 |
-
$state = 'data';
|
560 |
-
|
561 |
-
} else {
|
562 |
-
/* Parse error. Switch to the bogus comment state. */
|
563 |
-
$this->emitToken([
|
564 |
-
'type' => self::PARSEERROR,
|
565 |
-
'data' => 'expected-closing-tag-but-got-char'
|
566 |
-
]);
|
567 |
-
$this->token = [
|
568 |
-
'data' => $char,
|
569 |
-
'type' => self::COMMENT
|
570 |
-
];
|
571 |
-
$state = 'bogus comment';
|
572 |
-
}
|
573 |
-
}
|
574 |
-
break;
|
575 |
-
|
576 |
-
case 'tag name':
|
577 |
-
/* Consume the next input character: */
|
578 |
-
$char = $this->stream->char();
|
579 |
-
|
580 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
581 |
-
/* U+0009 CHARACTER TABULATION
|
582 |
-
U+000A LINE FEED (LF)
|
583 |
-
U+000C FORM FEED (FF)
|
584 |
-
U+0020 SPACE
|
585 |
-
Switch to the before attribute name state. */
|
586 |
-
$state = 'before attribute name';
|
587 |
-
|
588 |
-
} elseif ($char === '/') {
|
589 |
-
/* U+002F SOLIDUS (/)
|
590 |
-
Switch to the self-closing start tag state. */
|
591 |
-
$state = 'self-closing start tag';
|
592 |
-
|
593 |
-
} elseif ($char === '>') {
|
594 |
-
/* U+003E GREATER-THAN SIGN (>)
|
595 |
-
Emit the current tag token. Switch to the data state. */
|
596 |
-
$this->emitToken($this->token);
|
597 |
-
$state = 'data';
|
598 |
-
|
599 |
-
} elseif ('A' <= $char && $char <= 'Z') {
|
600 |
-
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
601 |
-
Append the lowercase version of the current input
|
602 |
-
character (add 0x0020 to the character's code point) to
|
603 |
-
the current tag token's tag name. Stay in the tag name state. */
|
604 |
-
$chars = $this->stream->charsWhile(self::UPPER_ALPHA);
|
605 |
-
|
606 |
-
$this->token['name'] .= strtolower($char . $chars);
|
607 |
-
$state = 'tag name';
|
608 |
-
|
609 |
-
} elseif ($char === false) {
|
610 |
-
/* EOF
|
611 |
-
Parse error. Reconsume the EOF character in the data state. */
|
612 |
-
$this->emitToken([
|
613 |
-
'type' => self::PARSEERROR,
|
614 |
-
'data' => 'eof-in-tag-name'
|
615 |
-
]);
|
616 |
-
|
617 |
-
$this->stream->unget();
|
618 |
-
$state = 'data';
|
619 |
-
|
620 |
-
} else {
|
621 |
-
/* Anything else
|
622 |
-
Append the current input character to the current tag token's tag name.
|
623 |
-
Stay in the tag name state. */
|
624 |
-
$chars = $this->stream->charsUntil("\t\n\x0C />" . self::UPPER_ALPHA);
|
625 |
-
|
626 |
-
$this->token['name'] .= $char . $chars;
|
627 |
-
$state = 'tag name';
|
628 |
-
}
|
629 |
-
break;
|
630 |
-
|
631 |
-
case 'before attribute name':
|
632 |
-
/* Consume the next input character: */
|
633 |
-
$char = $this->stream->char();
|
634 |
-
|
635 |
-
// this conditional is optimized, check bottom
|
636 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
637 |
-
/* U+0009 CHARACTER TABULATION
|
638 |
-
U+000A LINE FEED (LF)
|
639 |
-
U+000C FORM FEED (FF)
|
640 |
-
U+0020 SPACE
|
641 |
-
Stay in the before attribute name state. */
|
642 |
-
$state = 'before attribute name';
|
643 |
-
|
644 |
-
} elseif ($char === '/') {
|
645 |
-
/* U+002F SOLIDUS (/)
|
646 |
-
Switch to the self-closing start tag state. */
|
647 |
-
$state = 'self-closing start tag';
|
648 |
-
|
649 |
-
} elseif ($char === '>') {
|
650 |
-
/* U+003E GREATER-THAN SIGN (>)
|
651 |
-
Emit the current tag token. Switch to the data state. */
|
652 |
-
$this->emitToken($this->token);
|
653 |
-
$state = 'data';
|
654 |
-
|
655 |
-
} elseif ('A' <= $char && $char <= 'Z') {
|
656 |
-
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
657 |
-
Start a new attribute in the current tag token. Set that
|
658 |
-
attribute's name to the lowercase version of the current
|
659 |
-
input character (add 0x0020 to the character's code
|
660 |
-
point), and its value to the empty string. Switch to the
|
661 |
-
attribute name state.*/
|
662 |
-
$this->token['attr'][] = [
|
663 |
-
'name' => strtolower($char),
|
664 |
-
'value' => ''
|
665 |
-
];
|
666 |
-
|
667 |
-
$state = 'attribute name';
|
668 |
-
|
669 |
-
} elseif ($char === false) {
|
670 |
-
/* EOF
|
671 |
-
Parse error. Reconsume the EOF character in the data state. */
|
672 |
-
$this->emitToken([
|
673 |
-
'type' => self::PARSEERROR,
|
674 |
-
'data' => 'expected-attribute-name-but-got-eof'
|
675 |
-
]);
|
676 |
-
|
677 |
-
$this->stream->unget();
|
678 |
-
$state = 'data';
|
679 |
-
|
680 |
-
} else {
|
681 |
-
/* U+0022 QUOTATION MARK (")
|
682 |
-
U+0027 APOSTROPHE (')
|
683 |
-
U+003C LESS-THAN SIGN (<)
|
684 |
-
U+003D EQUALS SIGN (=)
|
685 |
-
Parse error. Treat it as per the "anything else" entry
|
686 |
-
below. */
|
687 |
-
if ($char === '"' || $char === "'" || $char === '<' || $char === '=') {
|
688 |
-
$this->emitToken([
|
689 |
-
'type' => self::PARSEERROR,
|
690 |
-
'data' => 'invalid-character-in-attribute-name'
|
691 |
-
]);
|
692 |
-
}
|
693 |
-
|
694 |
-
/* Anything else
|
695 |
-
Start a new attribute in the current tag token. Set that attribute's
|
696 |
-
name to the current input character, and its value to the empty string.
|
697 |
-
Switch to the attribute name state. */
|
698 |
-
$this->token['attr'][] = [
|
699 |
-
'name' => $char,
|
700 |
-
'value' => ''
|
701 |
-
];
|
702 |
-
|
703 |
-
$state = 'attribute name';
|
704 |
-
}
|
705 |
-
break;
|
706 |
-
|
707 |
-
case 'attribute name':
|
708 |
-
// Consume the next input character:
|
709 |
-
$char = $this->stream->char();
|
710 |
-
|
711 |
-
// this conditional is optimized, check bottom
|
712 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
713 |
-
/* U+0009 CHARACTER TABULATION
|
714 |
-
U+000A LINE FEED (LF)
|
715 |
-
U+000C FORM FEED (FF)
|
716 |
-
U+0020 SPACE
|
717 |
-
Switch to the after attribute name state. */
|
718 |
-
$state = 'after attribute name';
|
719 |
-
|
720 |
-
} elseif ($char === '/') {
|
721 |
-
/* U+002F SOLIDUS (/)
|
722 |
-
Switch to the self-closing start tag state. */
|
723 |
-
$state = 'self-closing start tag';
|
724 |
-
|
725 |
-
} elseif ($char === '=') {
|
726 |
-
/* U+003D EQUALS SIGN (=)
|
727 |
-
Switch to the before attribute value state. */
|
728 |
-
$state = 'before attribute value';
|
729 |
-
|
730 |
-
} elseif ($char === '>') {
|
731 |
-
/* U+003E GREATER-THAN SIGN (>)
|
732 |
-
Emit the current tag token. Switch to the data state. */
|
733 |
-
$this->emitToken($this->token);
|
734 |
-
$state = 'data';
|
735 |
-
|
736 |
-
} elseif ('A' <= $char && $char <= 'Z') {
|
737 |
-
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
738 |
-
Append the lowercase version of the current input
|
739 |
-
character (add 0x0020 to the character's code point) to
|
740 |
-
the current attribute's name. Stay in the attribute name
|
741 |
-
state. */
|
742 |
-
$chars = $this->stream->charsWhile(self::UPPER_ALPHA);
|
743 |
-
|
744 |
-
$last = count($this->token['attr']) - 1;
|
745 |
-
$this->token['attr'][$last]['name'] .= strtolower($char . $chars);
|
746 |
-
|
747 |
-
$state = 'attribute name';
|
748 |
-
|
749 |
-
} elseif ($char === false) {
|
750 |
-
/* EOF
|
751 |
-
Parse error. Reconsume the EOF character in the data state. */
|
752 |
-
$this->emitToken([
|
753 |
-
'type' => self::PARSEERROR,
|
754 |
-
'data' => 'eof-in-attribute-name'
|
755 |
-
]);
|
756 |
-
|
757 |
-
$this->stream->unget();
|
758 |
-
$state = 'data';
|
759 |
-
|
760 |
-
} else {
|
761 |
-
/* U+0022 QUOTATION MARK (")
|
762 |
-
U+0027 APOSTROPHE (')
|
763 |
-
U+003C LESS-THAN SIGN (<)
|
764 |
-
Parse error. Treat it as per the "anything else"
|
765 |
-
entry below. */
|
766 |
-
if ($char === '"' || $char === "'" || $char === '<') {
|
767 |
-
$this->emitToken([
|
768 |
-
'type' => self::PARSEERROR,
|
769 |
-
'data' => 'invalid-character-in-attribute-name'
|
770 |
-
]);
|
771 |
-
}
|
772 |
-
|
773 |
-
/* Anything else
|
774 |
-
Append the current input character to the current attribute's name.
|
775 |
-
Stay in the attribute name state. */
|
776 |
-
$chars = $this->stream->charsUntil("\t\n\x0C /=>\"'" . self::UPPER_ALPHA);
|
777 |
-
|
778 |
-
$last = count($this->token['attr']) - 1;
|
779 |
-
$this->token['attr'][$last]['name'] .= $char . $chars;
|
780 |
-
|
781 |
-
$state = 'attribute name';
|
782 |
-
}
|
783 |
-
|
784 |
-
/* When the user agent leaves the attribute name state
|
785 |
-
(and before emitting the tag token, if appropriate), the
|
786 |
-
complete attribute's name must be compared to the other
|
787 |
-
attributes on the same token; if there is already an
|
788 |
-
attribute on the token with the exact same name, then this
|
789 |
-
is a parse error and the new attribute must be dropped, along
|
790 |
-
with the value that gets associated with it (if any). */
|
791 |
-
// this might be implemented in the emitToken method
|
792 |
-
break;
|
793 |
-
|
794 |
-
case 'after attribute name':
|
795 |
-
// Consume the next input character:
|
796 |
-
$char = $this->stream->char();
|
797 |
-
|
798 |
-
// this is an optimized conditional, check the bottom
|
799 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
800 |
-
/* U+0009 CHARACTER TABULATION
|
801 |
-
U+000A LINE FEED (LF)
|
802 |
-
U+000C FORM FEED (FF)
|
803 |
-
U+0020 SPACE
|
804 |
-
Stay in the after attribute name state. */
|
805 |
-
$state = 'after attribute name';
|
806 |
-
|
807 |
-
} elseif ($char === '/') {
|
808 |
-
/* U+002F SOLIDUS (/)
|
809 |
-
Switch to the self-closing start tag state. */
|
810 |
-
$state = 'self-closing start tag';
|
811 |
-
|
812 |
-
} elseif ($char === '=') {
|
813 |
-
/* U+003D EQUALS SIGN (=)
|
814 |
-
Switch to the before attribute value state. */
|
815 |
-
$state = 'before attribute value';
|
816 |
-
|
817 |
-
} elseif ($char === '>') {
|
818 |
-
/* U+003E GREATER-THAN SIGN (>)
|
819 |
-
Emit the current tag token. Switch to the data state. */
|
820 |
-
$this->emitToken($this->token);
|
821 |
-
$state = 'data';
|
822 |
-
|
823 |
-
} elseif ('A' <= $char && $char <= 'Z') {
|
824 |
-
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
825 |
-
Start a new attribute in the current tag token. Set that
|
826 |
-
attribute's name to the lowercase version of the current
|
827 |
-
input character (add 0x0020 to the character's code
|
828 |
-
point), and its value to the empty string. Switch to the
|
829 |
-
attribute name state. */
|
830 |
-
$this->token['attr'][] = [
|
831 |
-
'name' => strtolower($char),
|
832 |
-
'value' => ''
|
833 |
-
];
|
834 |
-
|
835 |
-
$state = 'attribute name';
|
836 |
-
|
837 |
-
} elseif ($char === false) {
|
838 |
-
/* EOF
|
839 |
-
Parse error. Reconsume the EOF character in the data state. */
|
840 |
-
$this->emitToken([
|
841 |
-
'type' => self::PARSEERROR,
|
842 |
-
'data' => 'expected-end-of-tag-but-got-eof'
|
843 |
-
]);
|
844 |
-
|
845 |
-
$this->stream->unget();
|
846 |
-
$state = 'data';
|
847 |
-
|
848 |
-
} else {
|
849 |
-
/* U+0022 QUOTATION MARK (")
|
850 |
-
U+0027 APOSTROPHE (')
|
851 |
-
U+003C LESS-THAN SIGN(<)
|
852 |
-
Parse error. Treat it as per the "anything else"
|
853 |
-
entry below. */
|
854 |
-
if ($char === '"' || $char === "'" || $char === "<") {
|
855 |
-
$this->emitToken([
|
856 |
-
'type' => self::PARSEERROR,
|
857 |
-
'data' => 'invalid-character-after-attribute-name'
|
858 |
-
]);
|
859 |
-
}
|
860 |
-
|
861 |
-
/* Anything else
|
862 |
-
Start a new attribute in the current tag token. Set that attribute's
|
863 |
-
name to the current input character, and its value to the empty string.
|
864 |
-
Switch to the attribute name state. */
|
865 |
-
$this->token['attr'][] = [
|
866 |
-
'name' => $char,
|
867 |
-
'value' => ''
|
868 |
-
];
|
869 |
-
|
870 |
-
$state = 'attribute name';
|
871 |
-
}
|
872 |
-
break;
|
873 |
-
|
874 |
-
case 'before attribute value':
|
875 |
-
// Consume the next input character:
|
876 |
-
$char = $this->stream->char();
|
877 |
-
|
878 |
-
// this is an optimized conditional
|
879 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
880 |
-
/* U+0009 CHARACTER TABULATION
|
881 |
-
U+000A LINE FEED (LF)
|
882 |
-
U+000C FORM FEED (FF)
|
883 |
-
U+0020 SPACE
|
884 |
-
Stay in the before attribute value state. */
|
885 |
-
$state = 'before attribute value';
|
886 |
-
|
887 |
-
} elseif ($char === '"') {
|
888 |
-
/* U+0022 QUOTATION MARK (")
|
889 |
-
Switch to the attribute value (double-quoted) state. */
|
890 |
-
$state = 'attribute value (double-quoted)';
|
891 |
-
|
892 |
-
} elseif ($char === '&') {
|
893 |
-
/* U+0026 AMPERSAND (&)
|
894 |
-
Switch to the attribute value (unquoted) state and reconsume
|
895 |
-
this input character. */
|
896 |
-
$this->stream->unget();
|
897 |
-
$state = 'attribute value (unquoted)';
|
898 |
-
|
899 |
-
} elseif ($char === '\'') {
|
900 |
-
/* U+0027 APOSTROPHE (')
|
901 |
-
Switch to the attribute value (single-quoted) state. */
|
902 |
-
$state = 'attribute value (single-quoted)';
|
903 |
-
|
904 |
-
} elseif ($char === '>') {
|
905 |
-
/* U+003E GREATER-THAN SIGN (>)
|
906 |
-
Parse error. Emit the current tag token. Switch to the data state. */
|
907 |
-
$this->emitToken([
|
908 |
-
'type' => self::PARSEERROR,
|
909 |
-
'data' => 'expected-attribute-value-but-got-right-bracket'
|
910 |
-
]);
|
911 |
-
$this->emitToken($this->token);
|
912 |
-
$state = 'data';
|
913 |
-
|
914 |
-
} elseif ($char === false) {
|
915 |
-
/* EOF
|
916 |
-
Parse error. Reconsume the EOF character in the data state. */
|
917 |
-
$this->emitToken([
|
918 |
-
'type' => self::PARSEERROR,
|
919 |
-
'data' => 'expected-attribute-value-but-got-eof'
|
920 |
-
]);
|
921 |
-
$this->stream->unget();
|
922 |
-
$state = 'data';
|
923 |
-
|
924 |
-
} else {
|
925 |
-
/* U+003D EQUALS SIGN (=)
|
926 |
-
* U+003C LESS-THAN SIGN (<)
|
927 |
-
Parse error. Treat it as per the "anything else" entry below. */
|
928 |
-
if ($char === '=' || $char === '<') {
|
929 |
-
$this->emitToken([
|
930 |
-
'type' => self::PARSEERROR,
|
931 |
-
'data' => 'equals-in-unquoted-attribute-value'
|
932 |
-
]);
|
933 |
-
}
|
934 |
-
|
935 |
-
/* Anything else
|
936 |
-
Append the current input character to the current attribute's value.
|
937 |
-
Switch to the attribute value (unquoted) state. */
|
938 |
-
$last = count($this->token['attr']) - 1;
|
939 |
-
$this->token['attr'][$last]['value'] .= $char;
|
940 |
-
|
941 |
-
$state = 'attribute value (unquoted)';
|
942 |
-
}
|
943 |
-
break;
|
944 |
-
|
945 |
-
case 'attribute value (double-quoted)':
|
946 |
-
// Consume the next input character:
|
947 |
-
$char = $this->stream->char();
|
948 |
-
|
949 |
-
if ($char === '"') {
|
950 |
-
/* U+0022 QUOTATION MARK (")
|
951 |
-
Switch to the after attribute value (quoted) state. */
|
952 |
-
$state = 'after attribute value (quoted)';
|
953 |
-
|
954 |
-
} elseif ($char === '&') {
|
955 |
-
/* U+0026 AMPERSAND (&)
|
956 |
-
Switch to the character reference in attribute value
|
957 |
-
state, with the additional allowed character
|
958 |
-
being U+0022 QUOTATION MARK ("). */
|
959 |
-
$this->characterReferenceInAttributeValue('"');
|
960 |
-
|
961 |
-
} elseif ($char === false) {
|
962 |
-
/* EOF
|
963 |
-
Parse error. Reconsume the EOF character in the data state. */
|
964 |
-
$this->emitToken([
|
965 |
-
'type' => self::PARSEERROR,
|
966 |
-
'data' => 'eof-in-attribute-value-double-quote'
|
967 |
-
]);
|
968 |
-
|
969 |
-
$this->stream->unget();
|
970 |
-
$state = 'data';
|
971 |
-
|
972 |
-
} else {
|
973 |
-
/* Anything else
|
974 |
-
Append the current input character to the current attribute's value.
|
975 |
-
Stay in the attribute value (double-quoted) state. */
|
976 |
-
$chars = $this->stream->charsUntil('"&');
|
977 |
-
|
978 |
-
$last = count($this->token['attr']) - 1;
|
979 |
-
$this->token['attr'][$last]['value'] .= $char . $chars;
|
980 |
-
|
981 |
-
$state = 'attribute value (double-quoted)';
|
982 |
-
}
|
983 |
-
break;
|
984 |
-
|
985 |
-
case 'attribute value (single-quoted)':
|
986 |
-
// Consume the next input character:
|
987 |
-
$char = $this->stream->char();
|
988 |
-
|
989 |
-
if ($char === "'") {
|
990 |
-
/* U+0022 QUOTATION MARK (')
|
991 |
-
Switch to the after attribute value state. */
|
992 |
-
$state = 'after attribute value (quoted)';
|
993 |
-
|
994 |
-
} elseif ($char === '&') {
|
995 |
-
/* U+0026 AMPERSAND (&)
|
996 |
-
Switch to the entity in attribute value state. */
|
997 |
-
$this->characterReferenceInAttributeValue("'");
|
998 |
-
|
999 |
-
} elseif ($char === false) {
|
1000 |
-
/* EOF
|
1001 |
-
Parse error. Reconsume the EOF character in the data state. */
|
1002 |
-
$this->emitToken([
|
1003 |
-
'type' => self::PARSEERROR,
|
1004 |
-
'data' => 'eof-in-attribute-value-single-quote'
|
1005 |
-
]);
|
1006 |
-
|
1007 |
-
$this->stream->unget();
|
1008 |
-
$state = 'data';
|
1009 |
-
|
1010 |
-
} else {
|
1011 |
-
/* Anything else
|
1012 |
-
Append the current input character to the current attribute's value.
|
1013 |
-
Stay in the attribute value (single-quoted) state. */
|
1014 |
-
$chars = $this->stream->charsUntil("'&");
|
1015 |
-
|
1016 |
-
$last = count($this->token['attr']) - 1;
|
1017 |
-
$this->token['attr'][$last]['value'] .= $char . $chars;
|
1018 |
-
|
1019 |
-
$state = 'attribute value (single-quoted)';
|
1020 |
-
}
|
1021 |
-
break;
|
1022 |
-
|
1023 |
-
case 'attribute value (unquoted)':
|
1024 |
-
// Consume the next input character:
|
1025 |
-
$char = $this->stream->char();
|
1026 |
-
|
1027 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1028 |
-
/* U+0009 CHARACTER TABULATION
|
1029 |
-
U+000A LINE FEED (LF)
|
1030 |
-
U+000C FORM FEED (FF)
|
1031 |
-
U+0020 SPACE
|
1032 |
-
Switch to the before attribute name state. */
|
1033 |
-
$state = 'before attribute name';
|
1034 |
-
|
1035 |
-
} elseif ($char === '&') {
|
1036 |
-
/* U+0026 AMPERSAND (&)
|
1037 |
-
Switch to the entity in attribute value state, with the
|
1038 |
-
additional allowed character being U+003E
|
1039 |
-
GREATER-THAN SIGN (>). */
|
1040 |
-
$this->characterReferenceInAttributeValue('>');
|
1041 |
-
|
1042 |
-
} elseif ($char === '>') {
|
1043 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1044 |
-
Emit the current tag token. Switch to the data state. */
|
1045 |
-
$this->emitToken($this->token);
|
1046 |
-
$state = 'data';
|
1047 |
-
|
1048 |
-
} elseif ($char === false) {
|
1049 |
-
/* EOF
|
1050 |
-
Parse error. Reconsume the EOF character in the data state. */
|
1051 |
-
$this->emitToken([
|
1052 |
-
'type' => self::PARSEERROR,
|
1053 |
-
'data' => 'eof-in-attribute-value-no-quotes'
|
1054 |
-
]);
|
1055 |
-
$this->stream->unget();
|
1056 |
-
$state = 'data';
|
1057 |
-
|
1058 |
-
} else {
|
1059 |
-
/* U+0022 QUOTATION MARK (")
|
1060 |
-
U+0027 APOSTROPHE (')
|
1061 |
-
U+003C LESS-THAN SIGN (<)
|
1062 |
-
U+003D EQUALS SIGN (=)
|
1063 |
-
Parse error. Treat it as per the "anything else"
|
1064 |
-
entry below. */
|
1065 |
-
if ($char === '"' || $char === "'" || $char === '=' || $char == '<') {
|
1066 |
-
$this->emitToken([
|
1067 |
-
'type' => self::PARSEERROR,
|
1068 |
-
'data' => 'unexpected-character-in-unquoted-attribute-value'
|
1069 |
-
]);
|
1070 |
-
}
|
1071 |
-
|
1072 |
-
/* Anything else
|
1073 |
-
Append the current input character to the current attribute's value.
|
1074 |
-
Stay in the attribute value (unquoted) state. */
|
1075 |
-
$chars = $this->stream->charsUntil("\t\n\x0c &>\"'=");
|
1076 |
-
|
1077 |
-
$last = count($this->token['attr']) - 1;
|
1078 |
-
$this->token['attr'][$last]['value'] .= $char . $chars;
|
1079 |
-
|
1080 |
-
$state = 'attribute value (unquoted)';
|
1081 |
-
}
|
1082 |
-
break;
|
1083 |
-
|
1084 |
-
case 'after attribute value (quoted)':
|
1085 |
-
/* Consume the next input character: */
|
1086 |
-
$char = $this->stream->char();
|
1087 |
-
|
1088 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1089 |
-
/* U+0009 CHARACTER TABULATION
|
1090 |
-
U+000A LINE FEED (LF)
|
1091 |
-
U+000C FORM FEED (FF)
|
1092 |
-
U+0020 SPACE
|
1093 |
-
Switch to the before attribute name state. */
|
1094 |
-
$state = 'before attribute name';
|
1095 |
-
|
1096 |
-
} elseif ($char === '/') {
|
1097 |
-
/* U+002F SOLIDUS (/)
|
1098 |
-
Switch to the self-closing start tag state. */
|
1099 |
-
$state = 'self-closing start tag';
|
1100 |
-
|
1101 |
-
} elseif ($char === '>') {
|
1102 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1103 |
-
Emit the current tag token. Switch to the data state. */
|
1104 |
-
$this->emitToken($this->token);
|
1105 |
-
$state = 'data';
|
1106 |
-
|
1107 |
-
} elseif ($char === false) {
|
1108 |
-
/* EOF
|
1109 |
-
Parse error. Reconsume the EOF character in the data state. */
|
1110 |
-
$this->emitToken([
|
1111 |
-
'type' => self::PARSEERROR,
|
1112 |
-
'data' => 'unexpected-EOF-after-attribute-value'
|
1113 |
-
]);
|
1114 |
-
$this->stream->unget();
|
1115 |
-
$state = 'data';
|
1116 |
-
|
1117 |
-
} else {
|
1118 |
-
/* Anything else
|
1119 |
-
Parse error. Reconsume the character in the before attribute
|
1120 |
-
name state. */
|
1121 |
-
$this->emitToken([
|
1122 |
-
'type' => self::PARSEERROR,
|
1123 |
-
'data' => 'unexpected-character-after-attribute-value'
|
1124 |
-
]);
|
1125 |
-
$this->stream->unget();
|
1126 |
-
$state = 'before attribute name';
|
1127 |
-
}
|
1128 |
-
break;
|
1129 |
-
|
1130 |
-
case 'self-closing start tag':
|
1131 |
-
/* Consume the next input character: */
|
1132 |
-
$char = $this->stream->char();
|
1133 |
-
|
1134 |
-
if ($char === '>') {
|
1135 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1136 |
-
Set the self-closing flag of the current tag token.
|
1137 |
-
Emit the current tag token. Switch to the data state. */
|
1138 |
-
// not sure if this is the name we want
|
1139 |
-
$this->token['self-closing'] = true;
|
1140 |
-
$this->emitToken($this->token);
|
1141 |
-
$state = 'data';
|
1142 |
-
|
1143 |
-
} elseif ($char === false) {
|
1144 |
-
/* EOF
|
1145 |
-
Parse error. Reconsume the EOF character in the data state. */
|
1146 |
-
$this->emitToken([
|
1147 |
-
'type' => self::PARSEERROR,
|
1148 |
-
'data' => 'unexpected-eof-after-self-closing'
|
1149 |
-
]);
|
1150 |
-
$this->stream->unget();
|
1151 |
-
$state = 'data';
|
1152 |
-
|
1153 |
-
} else {
|
1154 |
-
/* Anything else
|
1155 |
-
Parse error. Reconsume the character in the before attribute name state. */
|
1156 |
-
$this->emitToken([
|
1157 |
-
'type' => self::PARSEERROR,
|
1158 |
-
'data' => 'unexpected-character-after-self-closing'
|
1159 |
-
]);
|
1160 |
-
$this->stream->unget();
|
1161 |
-
$state = 'before attribute name';
|
1162 |
-
}
|
1163 |
-
break;
|
1164 |
-
|
1165 |
-
case 'bogus comment':
|
1166 |
-
/* (This can only happen if the content model flag is set to the PCDATA state.) */
|
1167 |
-
/* Consume every character up to the first U+003E GREATER-THAN SIGN
|
1168 |
-
character (>) or the end of the file (EOF), whichever comes first. Emit
|
1169 |
-
a comment token whose data is the concatenation of all the characters
|
1170 |
-
starting from and including the character that caused the state machine
|
1171 |
-
to switch into the bogus comment state, up to and including the last
|
1172 |
-
consumed character before the U+003E character, if any, or up to the
|
1173 |
-
end of the file otherwise. (If the comment was started by the end of
|
1174 |
-
the file (EOF), the token is empty.) */
|
1175 |
-
$this->token['data'] .= (string) $this->stream->charsUntil('>');
|
1176 |
-
$this->stream->char();
|
1177 |
-
|
1178 |
-
$this->emitToken($this->token);
|
1179 |
-
|
1180 |
-
/* Switch to the data state. */
|
1181 |
-
$state = 'data';
|
1182 |
-
break;
|
1183 |
-
|
1184 |
-
case 'markup declaration open':
|
1185 |
-
// Consume for below
|
1186 |
-
$hyphens = $this->stream->charsWhile('-', 2);
|
1187 |
-
if ($hyphens === '-') {
|
1188 |
-
$this->stream->unget();
|
1189 |
-
}
|
1190 |
-
if ($hyphens !== '--') {
|
1191 |
-
$alpha = $this->stream->charsWhile(self::ALPHA, 7);
|
1192 |
-
}
|
1193 |
-
|
1194 |
-
/* If the next two characters are both U+002D HYPHEN-MINUS (-)
|
1195 |
-
characters, consume those two characters, create a comment token whose
|
1196 |
-
data is the empty string, and switch to the comment state. */
|
1197 |
-
if ($hyphens === '--') {
|
1198 |
-
$state = 'comment start';
|
1199 |
-
$this->token = [
|
1200 |
-
'data' => '',
|
1201 |
-
'type' => self::COMMENT
|
1202 |
-
];
|
1203 |
-
|
1204 |
-
/* Otherwise if the next seven characters are a case-insensitive match
|
1205 |
-
for the word "DOCTYPE", then consume those characters and switch to the
|
1206 |
-
DOCTYPE state. */
|
1207 |
-
} elseif (strtoupper($alpha) === 'DOCTYPE') {
|
1208 |
-
$state = 'DOCTYPE';
|
1209 |
-
|
1210 |
-
// XXX not implemented
|
1211 |
-
/* Otherwise, if the insertion mode is "in foreign content"
|
1212 |
-
and the current node is not an element in the HTML namespace
|
1213 |
-
and the next seven characters are an ASCII case-sensitive
|
1214 |
-
match for the string "[CDATA[" (the five uppercase letters
|
1215 |
-
"CDATA" with a U+005B LEFT SQUARE BRACKET character before
|
1216 |
-
and after), then consume those characters and switch to the
|
1217 |
-
CDATA section state (which is unrelated to the content model
|
1218 |
-
flag's CDATA state). */
|
1219 |
-
|
1220 |
-
/* Otherwise, is is a parse error. Switch to the bogus comment state.
|
1221 |
-
The next character that is consumed, if any, is the first character
|
1222 |
-
that will be in the comment. */
|
1223 |
-
} else {
|
1224 |
-
$this->emitToken([
|
1225 |
-
'type' => self::PARSEERROR,
|
1226 |
-
'data' => 'expected-dashes-or-doctype'
|
1227 |
-
]);
|
1228 |
-
$this->token = [
|
1229 |
-
'data' => (string) $alpha,
|
1230 |
-
'type' => self::COMMENT
|
1231 |
-
];
|
1232 |
-
$state = 'bogus comment';
|
1233 |
-
}
|
1234 |
-
break;
|
1235 |
-
|
1236 |
-
case 'comment start':
|
1237 |
-
/* Consume the next input character: */
|
1238 |
-
$char = $this->stream->char();
|
1239 |
-
|
1240 |
-
if ($char === '-') {
|
1241 |
-
/* U+002D HYPHEN-MINUS (-)
|
1242 |
-
Switch to the comment start dash state. */
|
1243 |
-
$state = 'comment start dash';
|
1244 |
-
} elseif ($char === '>') {
|
1245 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1246 |
-
Parse error. Emit the comment token. Switch to the
|
1247 |
-
data state. */
|
1248 |
-
$this->emitToken([
|
1249 |
-
'type' => self::PARSEERROR,
|
1250 |
-
'data' => 'incorrect-comment'
|
1251 |
-
]);
|
1252 |
-
$this->emitToken($this->token);
|
1253 |
-
$state = 'data';
|
1254 |
-
} elseif ($char === false) {
|
1255 |
-
/* EOF
|
1256 |
-
Parse error. Emit the comment token. Reconsume the
|
1257 |
-
EOF character in the data state. */
|
1258 |
-
$this->emitToken([
|
1259 |
-
'type' => self::PARSEERROR,
|
1260 |
-
'data' => 'eof-in-comment'
|
1261 |
-
]);
|
1262 |
-
$this->emitToken($this->token);
|
1263 |
-
$this->stream->unget();
|
1264 |
-
$state = 'data';
|
1265 |
-
} else {
|
1266 |
-
/* Anything else
|
1267 |
-
Append the input character to the comment token's
|
1268 |
-
data. Switch to the comment state. */
|
1269 |
-
$this->token['data'] .= $char;
|
1270 |
-
$state = 'comment';
|
1271 |
-
}
|
1272 |
-
break;
|
1273 |
-
|
1274 |
-
case 'comment start dash':
|
1275 |
-
/* Consume the next input character: */
|
1276 |
-
$char = $this->stream->char();
|
1277 |
-
if ($char === '-') {
|
1278 |
-
/* U+002D HYPHEN-MINUS (-)
|
1279 |
-
Switch to the comment end state */
|
1280 |
-
$state = 'comment end';
|
1281 |
-
} elseif ($char === '>') {
|
1282 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1283 |
-
Parse error. Emit the comment token. Switch to the
|
1284 |
-
data state. */
|
1285 |
-
$this->emitToken([
|
1286 |
-
'type' => self::PARSEERROR,
|
1287 |
-
'data' => 'incorrect-comment'
|
1288 |
-
]);
|
1289 |
-
$this->emitToken($this->token);
|
1290 |
-
$state = 'data';
|
1291 |
-
} elseif ($char === false) {
|
1292 |
-
/* Parse error. Emit the comment token. Reconsume the
|
1293 |
-
EOF character in the data state. */
|
1294 |
-
$this->emitToken([
|
1295 |
-
'type' => self::PARSEERROR,
|
1296 |
-
'data' => 'eof-in-comment'
|
1297 |
-
]);
|
1298 |
-
$this->emitToken($this->token);
|
1299 |
-
$this->stream->unget();
|
1300 |
-
$state = 'data';
|
1301 |
-
} else {
|
1302 |
-
$this->token['data'] .= '-' . $char;
|
1303 |
-
$state = 'comment';
|
1304 |
-
}
|
1305 |
-
break;
|
1306 |
-
|
1307 |
-
case 'comment':
|
1308 |
-
/* Consume the next input character: */
|
1309 |
-
$char = $this->stream->char();
|
1310 |
-
|
1311 |
-
if ($char === '-') {
|
1312 |
-
/* U+002D HYPHEN-MINUS (-)
|
1313 |
-
Switch to the comment end dash state */
|
1314 |
-
$state = 'comment end dash';
|
1315 |
-
|
1316 |
-
} elseif ($char === false) {
|
1317 |
-
/* EOF
|
1318 |
-
Parse error. Emit the comment token. Reconsume the EOF character
|
1319 |
-
in the data state. */
|
1320 |
-
$this->emitToken([
|
1321 |
-
'type' => self::PARSEERROR,
|
1322 |
-
'data' => 'eof-in-comment'
|
1323 |
-
]);
|
1324 |
-
$this->emitToken($this->token);
|
1325 |
-
$this->stream->unget();
|
1326 |
-
$state = 'data';
|
1327 |
-
|
1328 |
-
} else {
|
1329 |
-
/* Anything else
|
1330 |
-
Append the input character to the comment token's data. Stay in
|
1331 |
-
the comment state. */
|
1332 |
-
$chars = $this->stream->charsUntil('-');
|
1333 |
-
|
1334 |
-
$this->token['data'] .= $char . $chars;
|
1335 |
-
}
|
1336 |
-
break;
|
1337 |
-
|
1338 |
-
case 'comment end dash':
|
1339 |
-
/* Consume the next input character: */
|
1340 |
-
$char = $this->stream->char();
|
1341 |
-
|
1342 |
-
if ($char === '-') {
|
1343 |
-
/* U+002D HYPHEN-MINUS (-)
|
1344 |
-
Switch to the comment end state */
|
1345 |
-
$state = 'comment end';
|
1346 |
-
|
1347 |
-
} elseif ($char === false) {
|
1348 |
-
/* EOF
|
1349 |
-
Parse error. Emit the comment token. Reconsume the EOF character
|
1350 |
-
in the data state. */
|
1351 |
-
$this->emitToken([
|
1352 |
-
'type' => self::PARSEERROR,
|
1353 |
-
'data' => 'eof-in-comment-end-dash'
|
1354 |
-
]);
|
1355 |
-
$this->emitToken($this->token);
|
1356 |
-
$this->stream->unget();
|
1357 |
-
$state = 'data';
|
1358 |
-
|
1359 |
-
} else {
|
1360 |
-
/* Anything else
|
1361 |
-
Append a U+002D HYPHEN-MINUS (-) character and the input
|
1362 |
-
character to the comment token's data. Switch to the comment state. */
|
1363 |
-
$this->token['data'] .= '-'.$char;
|
1364 |
-
$state = 'comment';
|
1365 |
-
}
|
1366 |
-
break;
|
1367 |
-
|
1368 |
-
case 'comment end':
|
1369 |
-
/* Consume the next input character: */
|
1370 |
-
$char = $this->stream->char();
|
1371 |
-
|
1372 |
-
if ($char === '>') {
|
1373 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1374 |
-
Emit the comment token. Switch to the data state. */
|
1375 |
-
$this->emitToken($this->token);
|
1376 |
-
$state = 'data';
|
1377 |
-
|
1378 |
-
} elseif ($char === '-') {
|
1379 |
-
/* U+002D HYPHEN-MINUS (-)
|
1380 |
-
Parse error. Append a U+002D HYPHEN-MINUS (-) character
|
1381 |
-
to the comment token's data. Stay in the comment end
|
1382 |
-
state. */
|
1383 |
-
$this->emitToken([
|
1384 |
-
'type' => self::PARSEERROR,
|
1385 |
-
'data' => 'unexpected-dash-after-double-dash-in-comment'
|
1386 |
-
]);
|
1387 |
-
$this->token['data'] .= '-';
|
1388 |
-
|
1389 |
-
} elseif ($char === "\t" || $char === "\n" || $char === "\x0a" || $char === ' ') {
|
1390 |
-
$this->emitToken([
|
1391 |
-
'type' => self::PARSEERROR,
|
1392 |
-
'data' => 'unexpected-space-after-double-dash-in-comment'
|
1393 |
-
]);
|
1394 |
-
$this->token['data'] .= '--' . $char;
|
1395 |
-
$state = 'comment end space';
|
1396 |
-
|
1397 |
-
} elseif ($char === '!') {
|
1398 |
-
$this->emitToken([
|
1399 |
-
'type' => self::PARSEERROR,
|
1400 |
-
'data' => 'unexpected-bang-after-double-dash-in-comment'
|
1401 |
-
]);
|
1402 |
-
$state = 'comment end bang';
|
1403 |
-
|
1404 |
-
} elseif ($char === false) {
|
1405 |
-
/* EOF
|
1406 |
-
Parse error. Emit the comment token. Reconsume the
|
1407 |
-
EOF character in the data state. */
|
1408 |
-
$this->emitToken([
|
1409 |
-
'type' => self::PARSEERROR,
|
1410 |
-
'data' => 'eof-in-comment-double-dash'
|
1411 |
-
]);
|
1412 |
-
$this->emitToken($this->token);
|
1413 |
-
$this->stream->unget();
|
1414 |
-
$state = 'data';
|
1415 |
-
|
1416 |
-
} else {
|
1417 |
-
/* Anything else
|
1418 |
-
Parse error. Append two U+002D HYPHEN-MINUS (-)
|
1419 |
-
characters and the input character to the comment token's
|
1420 |
-
data. Switch to the comment state. */
|
1421 |
-
$this->emitToken([
|
1422 |
-
'type' => self::PARSEERROR,
|
1423 |
-
'data' => 'unexpected-char-in-comment'
|
1424 |
-
]);
|
1425 |
-
$this->token['data'] .= '--'.$char;
|
1426 |
-
$state = 'comment';
|
1427 |
-
}
|
1428 |
-
break;
|
1429 |
-
|
1430 |
-
case 'comment end bang':
|
1431 |
-
$char = $this->stream->char();
|
1432 |
-
if ($char === '>') {
|
1433 |
-
$this->emitToken($this->token);
|
1434 |
-
$state = 'data';
|
1435 |
-
} elseif ($char === "-") {
|
1436 |
-
$this->token['data'] .= '--!';
|
1437 |
-
$state = 'comment end dash';
|
1438 |
-
} elseif ($char === false) {
|
1439 |
-
$this->emitToken([
|
1440 |
-
'type' => self::PARSEERROR,
|
1441 |
-
'data' => 'eof-in-comment-end-bang'
|
1442 |
-
]);
|
1443 |
-
$this->emitToken($this->token);
|
1444 |
-
$this->stream->unget();
|
1445 |
-
$state = 'data';
|
1446 |
-
} else {
|
1447 |
-
$this->token['data'] .= '--!' . $char;
|
1448 |
-
$state = 'comment';
|
1449 |
-
}
|
1450 |
-
break;
|
1451 |
-
|
1452 |
-
case 'comment end space':
|
1453 |
-
$char = $this->stream->char();
|
1454 |
-
if ($char === '>') {
|
1455 |
-
$this->emitToken($this->token);
|
1456 |
-
$state = 'data';
|
1457 |
-
} elseif ($char === '-') {
|
1458 |
-
$state = 'comment end dash';
|
1459 |
-
} elseif ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1460 |
-
$this->token['data'] .= $char;
|
1461 |
-
} elseif ($char === false) {
|
1462 |
-
$this->emitToken([
|
1463 |
-
'type' => self::PARSEERROR,
|
1464 |
-
'data' => 'unexpected-eof-in-comment-end-space',
|
1465 |
-
]);
|
1466 |
-
$this->emitToken($this->token);
|
1467 |
-
$this->stream->unget();
|
1468 |
-
$state = 'data';
|
1469 |
-
} else {
|
1470 |
-
$this->token['data'] .= $char;
|
1471 |
-
$state = 'comment';
|
1472 |
-
}
|
1473 |
-
break;
|
1474 |
-
|
1475 |
-
case 'DOCTYPE':
|
1476 |
-
/* Consume the next input character: */
|
1477 |
-
$char = $this->stream->char();
|
1478 |
-
|
1479 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1480 |
-
/* U+0009 CHARACTER TABULATION
|
1481 |
-
U+000A LINE FEED (LF)
|
1482 |
-
U+000C FORM FEED (FF)
|
1483 |
-
U+0020 SPACE
|
1484 |
-
Switch to the before DOCTYPE name state. */
|
1485 |
-
$state = 'before DOCTYPE name';
|
1486 |
-
|
1487 |
-
} elseif ($char === false) {
|
1488 |
-
/* EOF
|
1489 |
-
Parse error. Create a new DOCTYPE token. Set its
|
1490 |
-
force-quirks flag to on. Emit the token. Reconsume the
|
1491 |
-
EOF character in the data state. */
|
1492 |
-
$this->emitToken([
|
1493 |
-
'type' => self::PARSEERROR,
|
1494 |
-
'data' => 'need-space-after-doctype-but-got-eof'
|
1495 |
-
]);
|
1496 |
-
$this->emitToken([
|
1497 |
-
'name' => '',
|
1498 |
-
'type' => self::DOCTYPE,
|
1499 |
-
'force-quirks' => true,
|
1500 |
-
'error' => true
|
1501 |
-
]);
|
1502 |
-
$this->stream->unget();
|
1503 |
-
$state = 'data';
|
1504 |
-
|
1505 |
-
} else {
|
1506 |
-
/* Anything else
|
1507 |
-
Parse error. Reconsume the current character in the
|
1508 |
-
before DOCTYPE name state. */
|
1509 |
-
$this->emitToken([
|
1510 |
-
'type' => self::PARSEERROR,
|
1511 |
-
'data' => 'need-space-after-doctype'
|
1512 |
-
]);
|
1513 |
-
$this->stream->unget();
|
1514 |
-
$state = 'before DOCTYPE name';
|
1515 |
-
}
|
1516 |
-
break;
|
1517 |
-
|
1518 |
-
case 'before DOCTYPE name':
|
1519 |
-
/* Consume the next input character: */
|
1520 |
-
$char = $this->stream->char();
|
1521 |
-
|
1522 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1523 |
-
/* U+0009 CHARACTER TABULATION
|
1524 |
-
U+000A LINE FEED (LF)
|
1525 |
-
U+000C FORM FEED (FF)
|
1526 |
-
U+0020 SPACE
|
1527 |
-
Stay in the before DOCTYPE name state. */
|
1528 |
-
|
1529 |
-
} elseif ($char === '>') {
|
1530 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1531 |
-
Parse error. Create a new DOCTYPE token. Set its
|
1532 |
-
force-quirks flag to on. Emit the token. Switch to the
|
1533 |
-
data state. */
|
1534 |
-
$this->emitToken([
|
1535 |
-
'type' => self::PARSEERROR,
|
1536 |
-
'data' => 'expected-doctype-name-but-got-right-bracket'
|
1537 |
-
]);
|
1538 |
-
$this->emitToken([
|
1539 |
-
'name' => '',
|
1540 |
-
'type' => self::DOCTYPE,
|
1541 |
-
'force-quirks' => true,
|
1542 |
-
'error' => true
|
1543 |
-
]);
|
1544 |
-
|
1545 |
-
$state = 'data';
|
1546 |
-
|
1547 |
-
} elseif ('A' <= $char && $char <= 'Z') {
|
1548 |
-
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
1549 |
-
Create a new DOCTYPE token. Set the token's name to the
|
1550 |
-
lowercase version of the input character (add 0x0020 to
|
1551 |
-
the character's code point). Switch to the DOCTYPE name
|
1552 |
-
state. */
|
1553 |
-
$this->token = [
|
1554 |
-
'name' => strtolower($char),
|
1555 |
-
'type' => self::DOCTYPE,
|
1556 |
-
'error' => true
|
1557 |
-
];
|
1558 |
-
|
1559 |
-
$state = 'DOCTYPE name';
|
1560 |
-
|
1561 |
-
} elseif ($char === false) {
|
1562 |
-
/* EOF
|
1563 |
-
Parse error. Create a new DOCTYPE token. Set its
|
1564 |
-
force-quirks flag to on. Emit the token. Reconsume the
|
1565 |
-
EOF character in the data state. */
|
1566 |
-
$this->emitToken([
|
1567 |
-
'type' => self::PARSEERROR,
|
1568 |
-
'data' => 'expected-doctype-name-but-got-eof'
|
1569 |
-
]);
|
1570 |
-
$this->emitToken([
|
1571 |
-
'name' => '',
|
1572 |
-
'type' => self::DOCTYPE,
|
1573 |
-
'force-quirks' => true,
|
1574 |
-
'error' => true
|
1575 |
-
]);
|
1576 |
-
|
1577 |
-
$this->stream->unget();
|
1578 |
-
$state = 'data';
|
1579 |
-
|
1580 |
-
} else {
|
1581 |
-
/* Anything else
|
1582 |
-
Create a new DOCTYPE token. Set the token's name to the
|
1583 |
-
current input character. Switch to the DOCTYPE name state. */
|
1584 |
-
$this->token = [
|
1585 |
-
'name' => $char,
|
1586 |
-
'type' => self::DOCTYPE,
|
1587 |
-
'error' => true
|
1588 |
-
];
|
1589 |
-
|
1590 |
-
$state = 'DOCTYPE name';
|
1591 |
-
}
|
1592 |
-
break;
|
1593 |
-
|
1594 |
-
case 'DOCTYPE name':
|
1595 |
-
/* Consume the next input character: */
|
1596 |
-
$char = $this->stream->char();
|
1597 |
-
|
1598 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1599 |
-
/* U+0009 CHARACTER TABULATION
|
1600 |
-
U+000A LINE FEED (LF)
|
1601 |
-
U+000C FORM FEED (FF)
|
1602 |
-
U+0020 SPACE
|
1603 |
-
Switch to the after DOCTYPE name state. */
|
1604 |
-
$state = 'after DOCTYPE name';
|
1605 |
-
|
1606 |
-
} elseif ($char === '>') {
|
1607 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1608 |
-
Emit the current DOCTYPE token. Switch to the data state. */
|
1609 |
-
$this->emitToken($this->token);
|
1610 |
-
$state = 'data';
|
1611 |
-
|
1612 |
-
} elseif ('A' <= $char && $char <= 'Z') {
|
1613 |
-
/* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z
|
1614 |
-
Append the lowercase version of the input character
|
1615 |
-
(add 0x0020 to the character's code point) to the current
|
1616 |
-
DOCTYPE token's name. Stay in the DOCTYPE name state. */
|
1617 |
-
$this->token['name'] .= strtolower($char);
|
1618 |
-
|
1619 |
-
} elseif ($char === false) {
|
1620 |
-
/* EOF
|
1621 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1622 |
-
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1623 |
-
character in the data state. */
|
1624 |
-
$this->emitToken([
|
1625 |
-
'type' => self::PARSEERROR,
|
1626 |
-
'data' => 'eof-in-doctype-name'
|
1627 |
-
]);
|
1628 |
-
$this->token['force-quirks'] = true;
|
1629 |
-
$this->emitToken($this->token);
|
1630 |
-
$this->stream->unget();
|
1631 |
-
$state = 'data';
|
1632 |
-
|
1633 |
-
} else {
|
1634 |
-
/* Anything else
|
1635 |
-
Append the current input character to the current
|
1636 |
-
DOCTYPE token's name. Stay in the DOCTYPE name state. */
|
1637 |
-
$this->token['name'] .= $char;
|
1638 |
-
}
|
1639 |
-
|
1640 |
-
// XXX this is probably some sort of quirks mode designation,
|
1641 |
-
// check tree-builder to be sure. In general 'error' needs
|
1642 |
-
// to be specc'ified, this probably means removing it at the end
|
1643 |
-
$this->token['error'] = ($this->token['name'] === 'HTML')
|
1644 |
-
? false
|
1645 |
-
: true;
|
1646 |
-
break;
|
1647 |
-
|
1648 |
-
case 'after DOCTYPE name':
|
1649 |
-
/* Consume the next input character: */
|
1650 |
-
$char = $this->stream->char();
|
1651 |
-
|
1652 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1653 |
-
/* U+0009 CHARACTER TABULATION
|
1654 |
-
U+000A LINE FEED (LF)
|
1655 |
-
U+000C FORM FEED (FF)
|
1656 |
-
U+0020 SPACE
|
1657 |
-
Stay in the after DOCTYPE name state. */
|
1658 |
-
|
1659 |
-
} elseif ($char === '>') {
|
1660 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1661 |
-
Emit the current DOCTYPE token. Switch to the data state. */
|
1662 |
-
$this->emitToken($this->token);
|
1663 |
-
$state = 'data';
|
1664 |
-
|
1665 |
-
} elseif ($char === false) {
|
1666 |
-
/* EOF
|
1667 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1668 |
-
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1669 |
-
character in the data state. */
|
1670 |
-
$this->emitToken([
|
1671 |
-
'type' => self::PARSEERROR,
|
1672 |
-
'data' => 'eof-in-doctype'
|
1673 |
-
]);
|
1674 |
-
$this->token['force-quirks'] = true;
|
1675 |
-
$this->emitToken($this->token);
|
1676 |
-
$this->stream->unget();
|
1677 |
-
$state = 'data';
|
1678 |
-
|
1679 |
-
} else {
|
1680 |
-
/* Anything else */
|
1681 |
-
|
1682 |
-
$nextSix = strtoupper($char . $this->stream->charsWhile(self::ALPHA, 5));
|
1683 |
-
if ($nextSix === 'PUBLIC') {
|
1684 |
-
/* If the next six characters are an ASCII
|
1685 |
-
case-insensitive match for the word "PUBLIC", then
|
1686 |
-
consume those characters and switch to the before
|
1687 |
-
DOCTYPE public identifier state. */
|
1688 |
-
$state = 'before DOCTYPE public identifier';
|
1689 |
-
|
1690 |
-
} elseif ($nextSix === 'SYSTEM') {
|
1691 |
-
/* Otherwise, if the next six characters are an ASCII
|
1692 |
-
case-insensitive match for the word "SYSTEM", then
|
1693 |
-
consume those characters and switch to the before
|
1694 |
-
DOCTYPE system identifier state. */
|
1695 |
-
$state = 'before DOCTYPE system identifier';
|
1696 |
-
|
1697 |
-
} else {
|
1698 |
-
/* Otherwise, this is the parse error. Set the DOCTYPE
|
1699 |
-
token's force-quirks flag to on. Switch to the bogus
|
1700 |
-
DOCTYPE state. */
|
1701 |
-
$this->emitToken([
|
1702 |
-
'type' => self::PARSEERROR,
|
1703 |
-
'data' => 'expected-space-or-right-bracket-in-doctype'
|
1704 |
-
]);
|
1705 |
-
$this->token['force-quirks'] = true;
|
1706 |
-
$this->token['error'] = true;
|
1707 |
-
$state = 'bogus DOCTYPE';
|
1708 |
-
}
|
1709 |
-
}
|
1710 |
-
break;
|
1711 |
-
|
1712 |
-
case 'before DOCTYPE public identifier':
|
1713 |
-
/* Consume the next input character: */
|
1714 |
-
$char = $this->stream->char();
|
1715 |
-
|
1716 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1717 |
-
/* U+0009 CHARACTER TABULATION
|
1718 |
-
U+000A LINE FEED (LF)
|
1719 |
-
U+000C FORM FEED (FF)
|
1720 |
-
U+0020 SPACE
|
1721 |
-
Stay in the before DOCTYPE public identifier state. */
|
1722 |
-
} elseif ($char === '"') {
|
1723 |
-
/* U+0022 QUOTATION MARK (")
|
1724 |
-
Set the DOCTYPE token's public identifier to the empty
|
1725 |
-
string (not missing), then switch to the DOCTYPE public
|
1726 |
-
identifier (double-quoted) state. */
|
1727 |
-
$this->token['public'] = '';
|
1728 |
-
$state = 'DOCTYPE public identifier (double-quoted)';
|
1729 |
-
} elseif ($char === "'") {
|
1730 |
-
/* U+0027 APOSTROPHE (')
|
1731 |
-
Set the DOCTYPE token's public identifier to the empty
|
1732 |
-
string (not missing), then switch to the DOCTYPE public
|
1733 |
-
identifier (single-quoted) state. */
|
1734 |
-
$this->token['public'] = '';
|
1735 |
-
$state = 'DOCTYPE public identifier (single-quoted)';
|
1736 |
-
} elseif ($char === '>') {
|
1737 |
-
/* Parse error. Set the DOCTYPE token's force-quirks flag
|
1738 |
-
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1739 |
-
$this->emitToken([
|
1740 |
-
'type' => self::PARSEERROR,
|
1741 |
-
'data' => 'unexpected-end-of-doctype'
|
1742 |
-
]);
|
1743 |
-
$this->token['force-quirks'] = true;
|
1744 |
-
$this->emitToken($this->token);
|
1745 |
-
$state = 'data';
|
1746 |
-
} elseif ($char === false) {
|
1747 |
-
/* Parse error. Set the DOCTYPE token's force-quirks
|
1748 |
-
flag to on. Emit that DOCTYPE token. Reconsume the EOF
|
1749 |
-
character in the data state. */
|
1750 |
-
$this->emitToken([
|
1751 |
-
'type' => self::PARSEERROR,
|
1752 |
-
'data' => 'eof-in-doctype'
|
1753 |
-
]);
|
1754 |
-
$this->token['force-quirks'] = true;
|
1755 |
-
$this->emitToken($this->token);
|
1756 |
-
$this->stream->unget();
|
1757 |
-
$state = 'data';
|
1758 |
-
} else {
|
1759 |
-
/* Parse error. Set the DOCTYPE token's force-quirks flag
|
1760 |
-
to on. Switch to the bogus DOCTYPE state. */
|
1761 |
-
$this->emitToken([
|
1762 |
-
'type' => self::PARSEERROR,
|
1763 |
-
'data' => 'unexpected-char-in-doctype'
|
1764 |
-
]);
|
1765 |
-
$this->token['force-quirks'] = true;
|
1766 |
-
$state = 'bogus DOCTYPE';
|
1767 |
-
}
|
1768 |
-
break;
|
1769 |
-
|
1770 |
-
case 'DOCTYPE public identifier (double-quoted)':
|
1771 |
-
/* Consume the next input character: */
|
1772 |
-
$char = $this->stream->char();
|
1773 |
-
|
1774 |
-
if ($char === '"') {
|
1775 |
-
/* U+0022 QUOTATION MARK (")
|
1776 |
-
Switch to the after DOCTYPE public identifier state. */
|
1777 |
-
$state = 'after DOCTYPE public identifier';
|
1778 |
-
} elseif ($char === '>') {
|
1779 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1780 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1781 |
-
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1782 |
-
$this->emitToken([
|
1783 |
-
'type' => self::PARSEERROR,
|
1784 |
-
'data' => 'unexpected-end-of-doctype'
|
1785 |
-
]);
|
1786 |
-
$this->token['force-quirks'] = true;
|
1787 |
-
$this->emitToken($this->token);
|
1788 |
-
$state = 'data';
|
1789 |
-
} elseif ($char === false) {
|
1790 |
-
/* EOF
|
1791 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1792 |
-
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1793 |
-
character in the data state. */
|
1794 |
-
$this->emitToken([
|
1795 |
-
'type' => self::PARSEERROR,
|
1796 |
-
'data' => 'eof-in-doctype'
|
1797 |
-
]);
|
1798 |
-
$this->token['force-quirks'] = true;
|
1799 |
-
$this->emitToken($this->token);
|
1800 |
-
$this->stream->unget();
|
1801 |
-
$state = 'data';
|
1802 |
-
} else {
|
1803 |
-
/* Anything else
|
1804 |
-
Append the current input character to the current
|
1805 |
-
DOCTYPE token's public identifier. Stay in the DOCTYPE
|
1806 |
-
public identifier (double-quoted) state. */
|
1807 |
-
$this->token['public'] .= $char;
|
1808 |
-
}
|
1809 |
-
break;
|
1810 |
-
|
1811 |
-
case 'DOCTYPE public identifier (single-quoted)':
|
1812 |
-
/* Consume the next input character: */
|
1813 |
-
$char = $this->stream->char();
|
1814 |
-
|
1815 |
-
if ($char === "'") {
|
1816 |
-
/* U+0027 APOSTROPHE (')
|
1817 |
-
Switch to the after DOCTYPE public identifier state. */
|
1818 |
-
$state = 'after DOCTYPE public identifier';
|
1819 |
-
} elseif ($char === '>') {
|
1820 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1821 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1822 |
-
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1823 |
-
$this->emitToken([
|
1824 |
-
'type' => self::PARSEERROR,
|
1825 |
-
'data' => 'unexpected-end-of-doctype'
|
1826 |
-
]);
|
1827 |
-
$this->token['force-quirks'] = true;
|
1828 |
-
$this->emitToken($this->token);
|
1829 |
-
$state = 'data';
|
1830 |
-
} elseif ($char === false) {
|
1831 |
-
/* EOF
|
1832 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1833 |
-
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1834 |
-
character in the data state. */
|
1835 |
-
$this->emitToken([
|
1836 |
-
'type' => self::PARSEERROR,
|
1837 |
-
'data' => 'eof-in-doctype'
|
1838 |
-
]);
|
1839 |
-
$this->token['force-quirks'] = true;
|
1840 |
-
$this->emitToken($this->token);
|
1841 |
-
$this->stream->unget();
|
1842 |
-
$state = 'data';
|
1843 |
-
} else {
|
1844 |
-
/* Anything else
|
1845 |
-
Append the current input character to the current
|
1846 |
-
DOCTYPE token's public identifier. Stay in the DOCTYPE
|
1847 |
-
public identifier (double-quoted) state. */
|
1848 |
-
$this->token['public'] .= $char;
|
1849 |
-
}
|
1850 |
-
break;
|
1851 |
-
|
1852 |
-
case 'after DOCTYPE public identifier':
|
1853 |
-
/* Consume the next input character: */
|
1854 |
-
$char = $this->stream->char();
|
1855 |
-
|
1856 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1857 |
-
/* U+0009 CHARACTER TABULATION
|
1858 |
-
U+000A LINE FEED (LF)
|
1859 |
-
U+000C FORM FEED (FF)
|
1860 |
-
U+0020 SPACE
|
1861 |
-
Stay in the after DOCTYPE public identifier state. */
|
1862 |
-
} elseif ($char === '"') {
|
1863 |
-
/* U+0022 QUOTATION MARK (")
|
1864 |
-
Set the DOCTYPE token's system identifier to the
|
1865 |
-
empty string (not missing), then switch to the DOCTYPE
|
1866 |
-
system identifier (double-quoted) state. */
|
1867 |
-
$this->token['system'] = '';
|
1868 |
-
$state = 'DOCTYPE system identifier (double-quoted)';
|
1869 |
-
} elseif ($char === "'") {
|
1870 |
-
/* U+0027 APOSTROPHE (')
|
1871 |
-
Set the DOCTYPE token's system identifier to the
|
1872 |
-
empty string (not missing), then switch to the DOCTYPE
|
1873 |
-
system identifier (single-quoted) state. */
|
1874 |
-
$this->token['system'] = '';
|
1875 |
-
$state = 'DOCTYPE system identifier (single-quoted)';
|
1876 |
-
} elseif ($char === '>') {
|
1877 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1878 |
-
Emit the current DOCTYPE token. Switch to the data state. */
|
1879 |
-
$this->emitToken($this->token);
|
1880 |
-
$state = 'data';
|
1881 |
-
} elseif ($char === false) {
|
1882 |
-
/* Parse error. Set the DOCTYPE token's force-quirks
|
1883 |
-
flag to on. Emit that DOCTYPE token. Reconsume the EOF
|
1884 |
-
character in the data state. */
|
1885 |
-
$this->emitToken([
|
1886 |
-
'type' => self::PARSEERROR,
|
1887 |
-
'data' => 'eof-in-doctype'
|
1888 |
-
]);
|
1889 |
-
$this->token['force-quirks'] = true;
|
1890 |
-
$this->emitToken($this->token);
|
1891 |
-
$this->stream->unget();
|
1892 |
-
$state = 'data';
|
1893 |
-
} else {
|
1894 |
-
/* Anything else
|
1895 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1896 |
-
to on. Switch to the bogus DOCTYPE state. */
|
1897 |
-
$this->emitToken([
|
1898 |
-
'type' => self::PARSEERROR,
|
1899 |
-
'data' => 'unexpected-char-in-doctype'
|
1900 |
-
]);
|
1901 |
-
$this->token['force-quirks'] = true;
|
1902 |
-
$state = 'bogus DOCTYPE';
|
1903 |
-
}
|
1904 |
-
break;
|
1905 |
-
|
1906 |
-
case 'before DOCTYPE system identifier':
|
1907 |
-
/* Consume the next input character: */
|
1908 |
-
$char = $this->stream->char();
|
1909 |
-
|
1910 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
1911 |
-
/* U+0009 CHARACTER TABULATION
|
1912 |
-
U+000A LINE FEED (LF)
|
1913 |
-
U+000C FORM FEED (FF)
|
1914 |
-
U+0020 SPACE
|
1915 |
-
Stay in the before DOCTYPE system identifier state. */
|
1916 |
-
} elseif ($char === '"') {
|
1917 |
-
/* U+0022 QUOTATION MARK (")
|
1918 |
-
Set the DOCTYPE token's system identifier to the empty
|
1919 |
-
string (not missing), then switch to the DOCTYPE system
|
1920 |
-
identifier (double-quoted) state. */
|
1921 |
-
$this->token['system'] = '';
|
1922 |
-
$state = 'DOCTYPE system identifier (double-quoted)';
|
1923 |
-
} elseif ($char === "'") {
|
1924 |
-
/* U+0027 APOSTROPHE (')
|
1925 |
-
Set the DOCTYPE token's system identifier to the empty
|
1926 |
-
string (not missing), then switch to the DOCTYPE system
|
1927 |
-
identifier (single-quoted) state. */
|
1928 |
-
$this->token['system'] = '';
|
1929 |
-
$state = 'DOCTYPE system identifier (single-quoted)';
|
1930 |
-
} elseif ($char === '>') {
|
1931 |
-
/* Parse error. Set the DOCTYPE token's force-quirks flag
|
1932 |
-
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1933 |
-
$this->emitToken([
|
1934 |
-
'type' => self::PARSEERROR,
|
1935 |
-
'data' => 'unexpected-char-in-doctype'
|
1936 |
-
]);
|
1937 |
-
$this->token['force-quirks'] = true;
|
1938 |
-
$this->emitToken($this->token);
|
1939 |
-
$state = 'data';
|
1940 |
-
} elseif ($char === false) {
|
1941 |
-
/* Parse error. Set the DOCTYPE token's force-quirks
|
1942 |
-
flag to on. Emit that DOCTYPE token. Reconsume the EOF
|
1943 |
-
character in the data state. */
|
1944 |
-
$this->emitToken([
|
1945 |
-
'type' => self::PARSEERROR,
|
1946 |
-
'data' => 'eof-in-doctype'
|
1947 |
-
]);
|
1948 |
-
$this->token['force-quirks'] = true;
|
1949 |
-
$this->emitToken($this->token);
|
1950 |
-
$this->stream->unget();
|
1951 |
-
$state = 'data';
|
1952 |
-
} else {
|
1953 |
-
/* Parse error. Set the DOCTYPE token's force-quirks flag
|
1954 |
-
to on. Switch to the bogus DOCTYPE state. */
|
1955 |
-
$this->emitToken([
|
1956 |
-
'type' => self::PARSEERROR,
|
1957 |
-
'data' => 'unexpected-char-in-doctype'
|
1958 |
-
]);
|
1959 |
-
$this->token['force-quirks'] = true;
|
1960 |
-
$state = 'bogus DOCTYPE';
|
1961 |
-
}
|
1962 |
-
break;
|
1963 |
-
|
1964 |
-
case 'DOCTYPE system identifier (double-quoted)':
|
1965 |
-
/* Consume the next input character: */
|
1966 |
-
$char = $this->stream->char();
|
1967 |
-
|
1968 |
-
if ($char === '"') {
|
1969 |
-
/* U+0022 QUOTATION MARK (")
|
1970 |
-
Switch to the after DOCTYPE system identifier state. */
|
1971 |
-
$state = 'after DOCTYPE system identifier';
|
1972 |
-
} elseif ($char === '>') {
|
1973 |
-
/* U+003E GREATER-THAN SIGN (>)
|
1974 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1975 |
-
to on. Emit that DOCTYPE token. Switch to the data state. */
|
1976 |
-
$this->emitToken([
|
1977 |
-
'type' => self::PARSEERROR,
|
1978 |
-
'data' => 'unexpected-end-of-doctype'
|
1979 |
-
]);
|
1980 |
-
$this->token['force-quirks'] = true;
|
1981 |
-
$this->emitToken($this->token);
|
1982 |
-
$state = 'data';
|
1983 |
-
} elseif ($char === false) {
|
1984 |
-
/* EOF
|
1985 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
1986 |
-
to on. Emit that DOCTYPE token. Reconsume the EOF
|
1987 |
-
character in the data state. */
|
1988 |
-
$this->emitToken([
|
1989 |
-
'type' => self::PARSEERROR,
|
1990 |
-
'data' => 'eof-in-doctype'
|
1991 |
-
]);
|
1992 |
-
$this->token['force-quirks'] = true;
|
1993 |
-
$this->emitToken($this->token);
|
1994 |
-
$this->stream->unget();
|
1995 |
-
$state = 'data';
|
1996 |
-
} else {
|
1997 |
-
/* Anything else
|
1998 |
-
Append the current input character to the current
|
1999 |
-
DOCTYPE token's system identifier. Stay in the DOCTYPE
|
2000 |
-
system identifier (double-quoted) state. */
|
2001 |
-
$this->token['system'] .= $char;
|
2002 |
-
}
|
2003 |
-
break;
|
2004 |
-
|
2005 |
-
case 'DOCTYPE system identifier (single-quoted)':
|
2006 |
-
/* Consume the next input character: */
|
2007 |
-
$char = $this->stream->char();
|
2008 |
-
|
2009 |
-
if ($char === "'") {
|
2010 |
-
/* U+0027 APOSTROPHE (')
|
2011 |
-
Switch to the after DOCTYPE system identifier state. */
|
2012 |
-
$state = 'after DOCTYPE system identifier';
|
2013 |
-
} elseif ($char === '>') {
|
2014 |
-
/* U+003E GREATER-THAN SIGN (>)
|
2015 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
2016 |
-
to on. Emit that DOCTYPE token. Switch to the data state. */
|
2017 |
-
$this->emitToken([
|
2018 |
-
'type' => self::PARSEERROR,
|
2019 |
-
'data' => 'unexpected-end-of-doctype'
|
2020 |
-
]);
|
2021 |
-
$this->token['force-quirks'] = true;
|
2022 |
-
$this->emitToken($this->token);
|
2023 |
-
$state = 'data';
|
2024 |
-
} elseif ($char === false) {
|
2025 |
-
/* EOF
|
2026 |
-
Parse error. Set the DOCTYPE token's force-quirks flag
|
2027 |
-
to on. Emit that DOCTYPE token. Reconsume the EOF
|
2028 |
-
character in the data state. */
|
2029 |
-
$this->emitToken([
|
2030 |
-
'type' => self::PARSEERROR,
|
2031 |
-
'data' => 'eof-in-doctype'
|
2032 |
-
]);
|
2033 |
-
$this->token['force-quirks'] = true;
|
2034 |
-
$this->emitToken($this->token);
|
2035 |
-
$this->stream->unget();
|
2036 |
-
$state = 'data';
|
2037 |
-
} else {
|
2038 |
-
/* Anything else
|
2039 |
-
Append the current input character to the current
|
2040 |
-
DOCTYPE token's system identifier. Stay in the DOCTYPE
|
2041 |
-
system identifier (double-quoted) state. */
|
2042 |
-
$this->token['system'] .= $char;
|
2043 |
-
}
|
2044 |
-
break;
|
2045 |
-
|
2046 |
-
case 'after DOCTYPE system identifier':
|
2047 |
-
/* Consume the next input character: */
|
2048 |
-
$char = $this->stream->char();
|
2049 |
-
|
2050 |
-
if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') {
|
2051 |
-
/* U+0009 CHARACTER TABULATION
|
2052 |
-
U+000A LINE FEED (LF)
|
2053 |
-
U+000C FORM FEED (FF)
|
2054 |
-
U+0020 SPACE
|
2055 |
-
Stay in the after DOCTYPE system identifier state. */
|
2056 |
-
} elseif ($char === '>') {
|
2057 |
-
/* U+003E GREATER-THAN SIGN (>)
|
2058 |
-
Emit the current DOCTYPE token. Switch to the data state. */
|
2059 |
-
$this->emitToken($this->token);
|
2060 |
-
$state = 'data';
|
2061 |
-
} elseif ($char === false) {
|
2062 |
-
/* Parse error. Set the DOCTYPE token's force-quirks
|
2063 |
-
flag to on. Emit that DOCTYPE token. Reconsume the EOF
|
2064 |
-
character in the data state. */
|
2065 |
-
$this->emitToken([
|
2066 |
-
'type' => self::PARSEERROR,
|
2067 |
-
'data' => 'eof-in-doctype'
|
2068 |
-
]);
|
2069 |
-
$this->token['force-quirks'] = true;
|
2070 |
-
$this->emitToken($this->token);
|
2071 |
-
$this->stream->unget();
|
2072 |
-
$state = 'data';
|
2073 |
-
} else {
|
2074 |
-
/* Anything else
|
2075 |
-
Parse error. Switch to the bogus DOCTYPE state.
|
2076 |
-
(This does not set the DOCTYPE token's force-quirks
|
2077 |
-
flag to on.) */
|
2078 |
-
$this->emitToken([
|
2079 |
-
'type' => self::PARSEERROR,
|
2080 |
-
'data' => 'unexpected-char-in-doctype'
|
2081 |
-
]);
|
2082 |
-
$state = 'bogus DOCTYPE';
|
2083 |
-
}
|
2084 |
-
break;
|
2085 |
-
|
2086 |
-
case 'bogus DOCTYPE':
|
2087 |
-
/* Consume the next input character: */
|
2088 |
-
$char = $this->stream->char();
|
2089 |
-
|
2090 |
-
if ($char === '>') {
|
2091 |
-
/* U+003E GREATER-THAN SIGN (>)
|
2092 |
-
Emit the DOCTYPE token. Switch to the data state. */
|
2093 |
-
$this->emitToken($this->token);
|
2094 |
-
$state = 'data';
|
2095 |
-
|
2096 |
-
} elseif ($char === false) {
|
2097 |
-
/* EOF
|
2098 |
-
Emit the DOCTYPE token. Reconsume the EOF character in
|
2099 |
-
the data state. */
|
2100 |
-
$this->emitToken($this->token);
|
2101 |
-
$this->stream->unget();
|
2102 |
-
$state = 'data';
|
2103 |
-
|
2104 |
-
} else {
|
2105 |
-
/* Anything else
|
2106 |
-
Stay in the bogus DOCTYPE state. */
|
2107 |
-
}
|
2108 |
-
break;
|
2109 |
-
|
2110 |
-
// case 'cdataSection':
|
2111 |
-
}
|
2112 |
-
}
|
2113 |
-
}
|
2114 |
-
|
2115 |
-
/**
|
2116 |
-
* Returns a serialized representation of the tree.
|
2117 |
-
*
|
2118 |
-
* @return DOMDocument|DOMNodeList
|
2119 |
-
*/
|
2120 |
-
public function save() {
|
2121 |
-
return $this->tree->save();
|
2122 |
-
}
|
2123 |
-
|
2124 |
-
/**
|
2125 |
-
* @return HTML5_TreeBuilder The tree
|
2126 |
-
*/
|
2127 |
-
public function getTree()
|
2128 |
-
{
|
2129 |
-
return $this->tree;
|
2130 |
-
}
|
2131 |
-
|
2132 |
-
|
2133 |
-
/**
|
2134 |
-
* Returns the input stream.
|
2135 |
-
*
|
2136 |
-
* @return HTML5_InputStream
|
2137 |
-
*/
|
2138 |
-
public function stream() {
|
2139 |
-
return $this->stream;
|
2140 |
-
}
|
2141 |
-
|
2142 |
-
/**
|
2143 |
-
* @param bool $allowed
|
2144 |
-
* @param bool $inattr
|
2145 |
-
* @return string
|
2146 |
-
*/
|
2147 |
-
private function consumeCharacterReference($allowed = false, $inattr = false) {
|
2148 |
-
// This goes quite far against spec, and is far closer to the Python
|
2149 |
-
// impl., mainly because we don't do the large unconsuming the spec
|
2150 |
-
// requires.
|
2151 |
-
|
2152 |
-
// All consumed characters.
|
2153 |
-
$chars = $this->stream->char();
|
2154 |
-
|
2155 |
-
/* This section defines how to consume a character
|
2156 |
-
reference. This definition is used when parsing character
|
2157 |
-
references in text and in attributes.
|
2158 |
-
|
2159 |
-
The behavior depends on the identity of the next character
|
2160 |
-
(the one immediately after the U+0026 AMPERSAND character): */
|
2161 |
-
|
2162 |
-
if (
|
2163 |
-
$chars[0] === "\x09" ||
|
2164 |
-
$chars[0] === "\x0A" ||
|
2165 |
-
$chars[0] === "\x0C" ||
|
2166 |
-
$chars[0] === "\x20" ||
|
2167 |
-
$chars[0] === '<' ||
|
2168 |
-
$chars[0] === '&' ||
|
2169 |
-
$chars === false ||
|
2170 |
-
$chars[0] === $allowed
|
2171 |
-
) {
|
2172 |
-
/* U+0009 CHARACTER TABULATION
|
2173 |
-
U+000A LINE FEED (LF)
|
2174 |
-
U+000C FORM FEED (FF)
|
2175 |
-
U+0020 SPACE
|
2176 |
-
U+003C LESS-THAN SIGN
|
2177 |
-
U+0026 AMPERSAND
|
2178 |
-
EOF
|
2179 |
-
The additional allowed character, if there is one
|
2180 |
-
Not a character reference. No characters are consumed,
|
2181 |
-
and nothing is returned. (This is not an error, either.) */
|
2182 |
-
// We already consumed, so unconsume.
|
2183 |
-
$this->stream->unget();
|
2184 |
-
return '&';
|
2185 |
-
} elseif ($chars[0] === '#') {
|
2186 |
-
/* Consume the U+0023 NUMBER SIGN. */
|
2187 |
-
// Um, yeah, we already did that.
|
2188 |
-
/* The behavior further depends on the character after
|
2189 |
-
the U+0023 NUMBER SIGN: */
|
2190 |
-
$chars .= $this->stream->char();
|
2191 |
-
if (isset($chars[1]) && ($chars[1] === 'x' || $chars[1] === 'X')) {
|
2192 |
-
/* U+0078 LATIN SMALL LETTER X
|
2193 |
-
U+0058 LATIN CAPITAL LETTER X */
|
2194 |
-
/* Consume the X. */
|
2195 |
-
// Um, yeah, we already did that.
|
2196 |
-
/* Follow the steps below, but using the range of
|
2197 |
-
characters U+0030 DIGIT ZERO through to U+0039 DIGIT
|
2198 |
-
NINE, U+0061 LATIN SMALL LETTER A through to U+0066
|
2199 |
-
LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER
|
2200 |
-
A, through to U+0046 LATIN CAPITAL LETTER F (in other
|
2201 |
-
words, 0123456789, ABCDEF, abcdef). */
|
2202 |
-
$char_class = self::HEX;
|
2203 |
-
/* When it comes to interpreting the
|
2204 |
-
number, interpret it as a hexadecimal number. */
|
2205 |
-
$hex = true;
|
2206 |
-
} else {
|
2207 |
-
/* Anything else */
|
2208 |
-
// Unconsume because we shouldn't have consumed this.
|
2209 |
-
$chars = $chars[0];
|
2210 |
-
$this->stream->unget();
|
2211 |
-
/* Follow the steps below, but using the range of
|
2212 |
-
characters U+0030 DIGIT ZERO through to U+0039 DIGIT
|
2213 |
-
NINE (i.e. just 0123456789). */
|
2214 |
-
$char_class = self::DIGIT;
|
2215 |
-
/* When it comes to interpreting the number,
|
2216 |
-
interpret it as a decimal number. */
|
2217 |
-
$hex = false;
|
2218 |
-
}
|
2219 |
-
|
2220 |
-
/* Consume as many characters as match the range of characters given above. */
|
2221 |
-
$consumed = $this->stream->charsWhile($char_class);
|
2222 |
-
if ($consumed === '' || $consumed === false) {
|
2223 |
-
/* If no characters match the range, then don't consume
|
2224 |
-
any characters (and unconsume the U+0023 NUMBER SIGN
|
2225 |
-
character and, if appropriate, the X character). This
|
2226 |
-
is a parse error; nothing is returned. */
|
2227 |
-
$this->emitToken([
|
2228 |
-
'type' => self::PARSEERROR,
|
2229 |
-
'data' => 'expected-numeric-entity'
|
2230 |
-
]);
|
2231 |
-
return '&' . $chars;
|
2232 |
-
} else {
|
2233 |
-
/* Otherwise, if the next character is a U+003B SEMICOLON,
|
2234 |
-
consume that too. If it isn't, there is a parse error. */
|
2235 |
-
if ($this->stream->char() !== ';') {
|
2236 |
-
$this->stream->unget();
|
2237 |
-
$this->emitToken([
|
2238 |
-
'type' => self::PARSEERROR,
|
2239 |
-
'data' => 'numeric-entity-without-semicolon'
|
2240 |
-
]);
|
2241 |
-
}
|
2242 |
-
|
2243 |
-
/* If one or more characters match the range, then take
|
2244 |
-
them all and interpret the string of characters as a number
|
2245 |
-
(either hexadecimal or decimal as appropriate). */
|
2246 |
-
$codepoint = $hex ? hexdec($consumed) : (int) $consumed;
|
2247 |
-
|
2248 |
-
/* If that number is one of the numbers in the first column
|
2249 |
-
of the following table, then this is a parse error. Find the
|
2250 |
-
row with that number in the first column, and return a
|
2251 |
-
character token for the Unicode character given in the
|
2252 |
-
second column of that row. */
|
2253 |
-
$new_codepoint = HTML5_Data::getRealCodepoint($codepoint);
|
2254 |
-
if ($new_codepoint) {
|
2255 |
-
$this->emitToken([
|
2256 |
-
'type' => self::PARSEERROR,
|
2257 |
-
'data' => 'illegal-windows-1252-entity'
|
2258 |
-
]);
|
2259 |
-
return HTML5_Data::utf8chr($new_codepoint);
|
2260 |
-
} else {
|
2261 |
-
/* Otherwise, if the number is greater than 0x10FFFF, then
|
2262 |
-
* this is a parse error. Return a U+FFFD REPLACEMENT
|
2263 |
-
* CHARACTER. */
|
2264 |
-
if ($codepoint > 0x10FFFF) {
|
2265 |
-
$this->emitToken([
|
2266 |
-
'type' => self::PARSEERROR,
|
2267 |
-
'data' => 'overlong-character-entity' // XXX probably not correct
|
2268 |
-
]);
|
2269 |
-
return "\xEF\xBF\xBD";
|
2270 |
-
}
|
2271 |
-
/* Otherwise, return a character token for the Unicode
|
2272 |
-
* character whose code point is that number. If the
|
2273 |
-
* number is in the range 0x0001 to 0x0008, 0x000E to
|
2274 |
-
* 0x001F, 0x007F to 0x009F, 0xD800 to 0xDFFF, 0xFDD0 to
|
2275 |
-
* 0xFDEF, or is one of 0x000B, 0xFFFE, 0xFFFF, 0x1FFFE,
|
2276 |
-
* 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE,
|
2277 |
-
* 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
|
2278 |
-
* 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE,
|
2279 |
-
* 0xAFFFF, 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE,
|
2280 |
-
* 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE,
|
2281 |
-
* or 0x10FFFF, then this is a parse error. */
|
2282 |
-
// && has higher precedence than ||
|
2283 |
-
if (
|
2284 |
-
$codepoint >= 0x0000 && $codepoint <= 0x0008 ||
|
2285 |
-
$codepoint === 0x000B ||
|
2286 |
-
$codepoint >= 0x000E && $codepoint <= 0x001F ||
|
2287 |
-
$codepoint >= 0x007F && $codepoint <= 0x009F ||
|
2288 |
-
$codepoint >= 0xD800 && $codepoint <= 0xDFFF ||
|
2289 |
-
$codepoint >= 0xFDD0 && $codepoint <= 0xFDEF ||
|
2290 |
-
($codepoint & 0xFFFE) === 0xFFFE ||
|
2291 |
-
$codepoint == 0x10FFFF || $codepoint == 0x10FFFE
|
2292 |
-
) {
|
2293 |
-
$this->emitToken([
|
2294 |
-
'type' => self::PARSEERROR,
|
2295 |
-
'data' => 'illegal-codepoint-for-numeric-entity'
|
2296 |
-
]);
|
2297 |
-
}
|
2298 |
-
return HTML5_Data::utf8chr($codepoint);
|
2299 |
-
}
|
2300 |
-
}
|
2301 |
-
} else {
|
2302 |
-
/* Anything else */
|
2303 |
-
|
2304 |
-
/* Consume the maximum number of characters possible,
|
2305 |
-
with the consumed characters matching one of the
|
2306 |
-
identifiers in the first column of the named character
|
2307 |
-
references table (in a case-sensitive manner). */
|
2308 |
-
// What we actually do here is consume as much as we can while it
|
2309 |
-
// matches the start of one of the identifiers in the first column.
|
2310 |
-
|
2311 |
-
$refs = HTML5_Data::getNamedCharacterReferences();
|
2312 |
-
|
2313 |
-
// Get the longest string which is the start of an identifier
|
2314 |
-
// ($chars) as well as the longest identifier which matches ($id)
|
2315 |
-
// and its codepoint ($codepoint).
|
2316 |
-
$codepoint = false;
|
2317 |
-
$char = $chars;
|
2318 |
-
while ($char !== false && isset($refs[$char])) {
|
2319 |
-
$refs = $refs[$char];
|
2320 |
-
if (isset($refs['codepoint'])) {
|
2321 |
-
$id = $chars;
|
2322 |
-
$codepoint = $refs['codepoint'];
|
2323 |
-
}
|
2324 |
-
$chars .= $char = $this->stream->char();
|
2325 |
-
}
|
2326 |
-
|
2327 |
-
// Unconsume the one character we just took which caused the while
|
2328 |
-
// statement to fail. This could be anything and could cause state
|
2329 |
-
// changes (as if it matches the while loop it must be
|
2330 |
-
// alphanumeric so we can just concat it to whatever we get later).
|
2331 |
-
$this->stream->unget();
|
2332 |
-
if ($char !== false) {
|
2333 |
-
$chars = substr($chars, 0, -1);
|
2334 |
-
}
|
2335 |
-
|
2336 |
-
/* If no match can be made, then this is a parse error.
|
2337 |
-
No characters are consumed, and nothing is returned. */
|
2338 |
-
if (!$codepoint) {
|
2339 |
-
$this->emitToken([
|
2340 |
-
'type' => self::PARSEERROR,
|
2341 |
-
'data' => 'expected-named-entity'
|
2342 |
-
]);
|
2343 |
-
return '&' . $chars;
|
2344 |
-
}
|
2345 |
-
|
2346 |
-
/* If the last character matched is not a U+003B SEMICOLON
|
2347 |
-
(;), there is a parse error. */
|
2348 |
-
$semicolon = true;
|
2349 |
-
if (substr($id, -1) !== ';') {
|
2350 |
-
$this->emitToken([
|
2351 |
-
'type' => self::PARSEERROR,
|
2352 |
-
'data' => 'named-entity-without-semicolon'
|
2353 |
-
]);
|
2354 |
-
$semicolon = false;
|
2355 |
-
}
|
2356 |
-
|
2357 |
-
/* If the character reference is being consumed as part of
|
2358 |
-
an attribute, and the last character matched is not a
|
2359 |
-
U+003B SEMICOLON (;), and the next character is in the
|
2360 |
-
range U+0030 DIGIT ZERO to U+0039 DIGIT NINE, U+0041
|
2361 |
-
LATIN CAPITAL LETTER A to U+005A LATIN CAPITAL LETTER Z,
|
2362 |
-
or U+0061 LATIN SMALL LETTER A to U+007A LATIN SMALL LETTER Z,
|
2363 |
-
then, for historical reasons, all the characters that were
|
2364 |
-
matched after the U+0026 AMPERSAND (&) must be unconsumed,
|
2365 |
-
and nothing is returned. */
|
2366 |
-
if ($inattr && !$semicolon) {
|
2367 |
-
// The next character is either the next character in $chars or in the stream.
|
2368 |
-
if (strlen($chars) > strlen($id)) {
|
2369 |
-
$next = substr($chars, strlen($id), 1);
|
2370 |
-
} else {
|
2371 |
-
$next = $this->stream->char();
|
2372 |
-
$this->stream->unget();
|
2373 |
-
}
|
2374 |
-
if (
|
2375 |
-
'0' <= $next && $next <= '9' ||
|
2376 |
-
'A' <= $next && $next <= 'Z' ||
|
2377 |
-
'a' <= $next && $next <= 'z'
|
2378 |
-
) {
|
2379 |
-
return '&' . $chars;
|
2380 |
-
}
|
2381 |
-
}
|
2382 |
-
|
2383 |
-
/* Otherwise, return a character token for the character
|
2384 |
-
corresponding to the character reference name (as given
|
2385 |
-
by the second column of the named character references table). */
|
2386 |
-
return HTML5_Data::utf8chr($codepoint) . substr($chars, strlen($id));
|
2387 |
-
}
|
2388 |
-
}
|
2389 |
-
|
2390 |
-
/**
|
2391 |
-
* @param bool $allowed
|
2392 |
-
*/
|
2393 |
-
private function characterReferenceInAttributeValue($allowed = false) {
|
2394 |
-
/* Attempt to consume a character reference. */
|
2395 |
-
$entity = $this->consumeCharacterReference($allowed, true);
|
2396 |
-
|
2397 |
-
/* If nothing is returned, append a U+0026 AMPERSAND
|
2398 |
-
character to the current attribute's value.
|
2399 |
-
|
2400 |
-
Otherwise, append the returned character token to the
|
2401 |
-
current attribute's value. */
|
2402 |
-
$char = (!$entity)
|
2403 |
-
? '&'
|
2404 |
-
: $entity;
|
2405 |
-
|
2406 |
-
$last = count($this->token['attr']) - 1;
|
2407 |
-
$this->token['attr'][$last]['value'] .= $char;
|
2408 |
-
|
2409 |
-
/* Finally, switch back to the attribute value state that you
|
2410 |
-
were in when were switched into this state. */
|
2411 |
-
}
|
2412 |
-
|
2413 |
-
/**
|
2414 |
-
* Emits a token, passing it on to the tree builder.
|
2415 |
-
*
|
2416 |
-
* @param $token
|
2417 |
-
* @param bool $checkStream
|
2418 |
-
* @param bool $dry
|
2419 |
-
*/
|
2420 |
-
protected function emitToken($token, $checkStream = true, $dry = false) {
|
2421 |
-
if ($checkStream === true) {
|
2422 |
-
// Emit errors from input stream.
|
2423 |
-
while ($this->stream->errors) {
|
2424 |
-
$this->emitToken(array_shift($this->stream->errors), false);
|
2425 |
-
}
|
2426 |
-
}
|
2427 |
-
if ($token['type'] === self::ENDTAG && !empty($token['attr'])) {
|
2428 |
-
for ($i = 0; $i < count($token['attr']); $i++) {
|
2429 |
-
$this->emitToken([
|
2430 |
-
'type' => self::PARSEERROR,
|
2431 |
-
'data' => 'attributes-in-end-tag'
|
2432 |
-
]);
|
2433 |
-
}
|
2434 |
-
}
|
2435 |
-
if ($token['type'] === self::ENDTAG && !empty($token['self-closing'])) {
|
2436 |
-
$this->emitToken([
|
2437 |
-
'type' => self::PARSEERROR,
|
2438 |
-
'data' => 'self-closing-flag-on-end-tag',
|
2439 |
-
]);
|
2440 |
-
}
|
2441 |
-
if ($token['type'] === self::STARTTAG) {
|
2442 |
-
// This could be changed to actually pass the tree-builder a hash
|
2443 |
-
$hash = [];
|
2444 |
-
foreach ($token['attr'] as $keypair) {
|
2445 |
-
if (isset($hash[$keypair['name']])) {
|
2446 |
-
$this->emitToken([
|
2447 |
-
'type' => self::PARSEERROR,
|
2448 |
-
'data' => 'duplicate-attribute',
|
2449 |
-
]);
|
2450 |
-
} else {
|
2451 |
-
$hash[$keypair['name']] = $keypair['value'];
|
2452 |
-
}
|
2453 |
-
}
|
2454 |
-
}
|
2455 |
-
|
2456 |
-
if ($dry === false) {
|
2457 |
-
// the current structure of attributes is not a terribly good one
|
2458 |
-
$this->tree->emitToken($token);
|
2459 |
-
}
|
2460 |
-
|
2461 |
-
if ($dry === false && is_int($this->tree->content_model)) {
|
2462 |
-
$this->content_model = $this->tree->content_model;
|
2463 |
-
$this->tree->content_model = null;
|
2464 |
-
|
2465 |
-
} elseif ($token['type'] === self::ENDTAG) {
|
2466 |
-
$this->content_model = self::PCDATA;
|
2467 |
-
}
|
2468 |
-
}
|
2469 |
-
}
|
2470 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/lib/html5lib/TreeBuilder.php
DELETED
@@ -1,3989 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*
|
4 |
-
|
5 |
-
Copyright 2007 Jeroen van der Meer <http://jero.net/>
|
6 |
-
Copyright 2009 Edward Z. Yang <edwardzyang@thewritingpot.com>
|
7 |
-
|
8 |
-
Permission is hereby granted, free of charge, to any person obtaining a
|
9 |
-
copy of this software and associated documentation files (the
|
10 |
-
"Software"), to deal in the Software without restriction, including
|
11 |
-
without limitation the rights to use, copy, modify, merge, publish,
|
12 |
-
distribute, sublicense, and/or sell copies of the Software, and to
|
13 |
-
permit persons to whom the Software is furnished to do so, subject to
|
14 |
-
the following conditions:
|
15 |
-
|
16 |
-
The above copyright notice and this permission notice shall be included
|
17 |
-
in all copies or substantial portions of the Software.
|
18 |
-
|
19 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
20 |
-
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21 |
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
22 |
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
23 |
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
24 |
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
25 |
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26 |
-
|
27 |
-
*/
|
28 |
-
|
29 |
-
// Tags for FIX ME!!!: (in order of priority)
|
30 |
-
// XXX - should be fixed NAO!
|
31 |
-
// XERROR - with regards to parse errors
|
32 |
-
// XSCRIPT - with regards to scripting mode
|
33 |
-
// XENCODING - with regards to encoding (for reparsing tests)
|
34 |
-
// XDOM - DOM specific code (tagName is explicitly not marked).
|
35 |
-
// this is not (yet) in helper functions.
|
36 |
-
|
37 |
-
class HTML5_TreeBuilder {
|
38 |
-
public $stack = [];
|
39 |
-
public $content_model;
|
40 |
-
|
41 |
-
private $mode;
|
42 |
-
private $original_mode;
|
43 |
-
private $secondary_mode;
|
44 |
-
private $dom;
|
45 |
-
// Whether or not normal insertion of nodes should actually foster
|
46 |
-
// parent (used in one case in spec)
|
47 |
-
private $foster_parent = false;
|
48 |
-
private $a_formatting = [];
|
49 |
-
|
50 |
-
private $head_pointer = null;
|
51 |
-
private $form_pointer = null;
|
52 |
-
|
53 |
-
private $flag_frameset_ok = true;
|
54 |
-
private $flag_force_quirks = false;
|
55 |
-
private $ignored = false;
|
56 |
-
private $quirks_mode = null;
|
57 |
-
// this gets to 2 when we want to ignore the next lf character, and
|
58 |
-
// is decrement at the beginning of each processed token (this way,
|
59 |
-
// code can check for (bool)$ignore_lf_token, but it phases out
|
60 |
-
// appropriately)
|
61 |
-
private $ignore_lf_token = 0;
|
62 |
-
private $fragment = false;
|
63 |
-
private $root;
|
64 |
-
|
65 |
-
private $scoping = ['applet','button','caption','html','marquee','object','table','td','th', 'svg:foreignObject'];
|
66 |
-
private $formatting = ['a','b','big','code','em','font','i','nobr','s','small','strike','strong','tt','u'];
|
67 |
-
// dl and ds are speculative
|
68 |
-
private $special = ['address','area','article','aside','base','basefont','bgsound',
|
69 |
-
'blockquote','body','br','center','col','colgroup','command','dc','dd','details','dir','div','dl','ds',
|
70 |
-
'dt','embed','fieldset','figure','footer','form','frame','frameset','h1','h2','h3','h4','h5',
|
71 |
-
'h6','head','header','hgroup','hr','iframe','img','input','isindex','li','link',
|
72 |
-
'listing','menu','meta','nav','noembed','noframes','noscript','ol',
|
73 |
-
'p','param','plaintext','pre','script','select','spacer','style',
|
74 |
-
'tbody','textarea','tfoot','thead','title','tr','ul','wbr'];
|
75 |
-
|
76 |
-
private $pendingTableCharacters;
|
77 |
-
private $pendingTableCharactersDirty;
|
78 |
-
|
79 |
-
// Tree construction modes
|
80 |
-
const INITIAL = 0;
|
81 |
-
const BEFORE_HTML = 1;
|
82 |
-
const BEFORE_HEAD = 2;
|
83 |
-
const IN_HEAD = 3;
|
84 |
-
const IN_HEAD_NOSCRIPT = 4;
|
85 |
-
const AFTER_HEAD = 5;
|
86 |
-
const IN_BODY = 6;
|
87 |
-
const IN_CDATA_RCDATA = 7;
|
88 |
-
const IN_TABLE = 8;
|
89 |
-
const IN_TABLE_TEXT = 9;
|
90 |
-
const IN_CAPTION = 10;
|
91 |
-
const IN_COLUMN_GROUP = 11;
|
92 |
-
const IN_TABLE_BODY = 12;
|
93 |
-
const IN_ROW = 13;
|
94 |
-
const IN_CELL = 14;
|
95 |
-
const IN_SELECT = 15;
|
96 |
-
const IN_SELECT_IN_TABLE= 16;
|
97 |
-
const IN_FOREIGN_CONTENT= 17;
|
98 |
-
const AFTER_BODY = 18;
|
99 |
-
const IN_FRAMESET = 19;
|
100 |
-
const AFTER_FRAMESET = 20;
|
101 |
-
const AFTER_AFTER_BODY = 21;
|
102 |
-
const AFTER_AFTER_FRAMESET = 22;
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Converts a magic number to a readable name. Use for debugging.
|
106 |
-
*/
|
107 |
-
private function strConst($number) {
|
108 |
-
static $lookup;
|
109 |
-
if (!$lookup) {
|
110 |
-
$lookup = [];
|
111 |
-
$r = new ReflectionClass('HTML5_TreeBuilder');
|
112 |
-
$consts = $r->getConstants();
|
113 |
-
foreach ($consts as $const => $num) {
|
114 |
-
if (!is_int($num)) {
|
115 |
-
continue;
|
116 |
-
}
|
117 |
-
$lookup[$num] = $const;
|
118 |
-
}
|
119 |
-
}
|
120 |
-
return $lookup[$number];
|
121 |
-
}
|
122 |
-
|
123 |
-
// The different types of elements.
|
124 |
-
const SPECIAL = 100;
|
125 |
-
const SCOPING = 101;
|
126 |
-
const FORMATTING = 102;
|
127 |
-
const PHRASING = 103;
|
128 |
-
|
129 |
-
// Quirks modes in $quirks_mode
|
130 |
-
const NO_QUIRKS = 200;
|
131 |
-
const QUIRKS_MODE = 201;
|
132 |
-
const LIMITED_QUIRKS_MODE = 202;
|
133 |
-
|
134 |
-
// Marker to be placed in $a_formatting
|
135 |
-
const MARKER = 300;
|
136 |
-
|
137 |
-
// Namespaces for foreign content
|
138 |
-
const NS_HTML = null; // to prevent DOM from requiring NS on everything
|
139 |
-
const NS_MATHML = 'http://www.w3.org/1998/Math/MathML';
|
140 |
-
const NS_SVG = 'http://www.w3.org/2000/svg';
|
141 |
-
const NS_XLINK = 'http://www.w3.org/1999/xlink';
|
142 |
-
const NS_XML = 'http://www.w3.org/XML/1998/namespace';
|
143 |
-
const NS_XMLNS = 'http://www.w3.org/2000/xmlns/';
|
144 |
-
|
145 |
-
// Different types of scopes to test for elements
|
146 |
-
const SCOPE = 0;
|
147 |
-
const SCOPE_LISTITEM = 1;
|
148 |
-
const SCOPE_TABLE = 2;
|
149 |
-
|
150 |
-
/**
|
151 |
-
* HTML5_TreeBuilder constructor.
|
152 |
-
*/
|
153 |
-
public function __construct() {
|
154 |
-
$this->mode = self::INITIAL;
|
155 |
-
$this->dom = new DOMDocument;
|
156 |
-
|
157 |
-
$this->dom->encoding = 'UTF-8';
|
158 |
-
$this->dom->preserveWhiteSpace = true;
|
159 |
-
$this->dom->substituteEntities = true;
|
160 |
-
$this->dom->strictErrorChecking = false;
|
161 |
-
}
|
162 |
-
|
163 |
-
public function getQuirksMode(){
|
164 |
-
return $this->quirks_mode;
|
165 |
-
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* Process tag tokens
|
169 |
-
*
|
170 |
-
* @param $token
|
171 |
-
* @param null $mode
|
172 |
-
*/
|
173 |
-
public function emitToken($token, $mode = null) {
|
174 |
-
// XXX: ignore parse errors... why are we emitting them, again?
|
175 |
-
if ($token['type'] === HTML5_Tokenizer::PARSEERROR) {
|
176 |
-
return;
|
177 |
-
}
|
178 |
-
if ($mode === null) {
|
179 |
-
$mode = $this->mode;
|
180 |
-
}
|
181 |
-
|
182 |
-
/*
|
183 |
-
$backtrace = debug_backtrace();
|
184 |
-
if ($backtrace[1]['class'] !== 'HTML5_TreeBuilder') echo "--\n";
|
185 |
-
echo $this->strConst($mode);
|
186 |
-
if ($this->original_mode) echo " (originally ".$this->strConst($this->original_mode).")";
|
187 |
-
echo "\n ";
|
188 |
-
token_dump($token);
|
189 |
-
$this->printStack();
|
190 |
-
$this->printActiveFormattingElements();
|
191 |
-
if ($this->foster_parent) echo " -> this is a foster parent mode\n";
|
192 |
-
if ($this->flag_frameset_ok) echo " -> frameset ok\n";
|
193 |
-
*/
|
194 |
-
|
195 |
-
if ($this->ignore_lf_token) {
|
196 |
-
$this->ignore_lf_token--;
|
197 |
-
}
|
198 |
-
$this->ignored = false;
|
199 |
-
|
200 |
-
switch ($mode) {
|
201 |
-
case self::INITIAL:
|
202 |
-
|
203 |
-
/* A character token that is one of U+0009 CHARACTER TABULATION,
|
204 |
-
* U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020 SPACE */
|
205 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
206 |
-
/* Ignore the token. */
|
207 |
-
$this->ignored = true;
|
208 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
209 |
-
if (
|
210 |
-
$token['name'] !== 'html' || !empty($token['public']) ||
|
211 |
-
!empty($token['system']) || $token !== 'about:legacy-compat'
|
212 |
-
) {
|
213 |
-
/* If the DOCTYPE token's name is not a case-sensitive match
|
214 |
-
* for the string "html", or if the token's public identifier
|
215 |
-
* is not missing, or if the token's system identifier is
|
216 |
-
* neither missing nor a case-sensitive match for the string
|
217 |
-
* "about:legacy-compat", then there is a parse error (this
|
218 |
-
* is the DOCTYPE parse error). */
|
219 |
-
// DOCTYPE parse error
|
220 |
-
}
|
221 |
-
/* Append a DocumentType node to the Document node, with the name
|
222 |
-
* attribute set to the name given in the DOCTYPE token, or the
|
223 |
-
* empty string if the name was missing; the publicId attribute
|
224 |
-
* set to the public identifier given in the DOCTYPE token, or
|
225 |
-
* the empty string if the public identifier was missing; the
|
226 |
-
* systemId attribute set to the system identifier given in the
|
227 |
-
* DOCTYPE token, or the empty string if the system identifier
|
228 |
-
* was missing; and the other attributes specific to
|
229 |
-
* DocumentType objects set to null and empty lists as
|
230 |
-
* appropriate. Associate the DocumentType node with the
|
231 |
-
* Document object so that it is returned as the value of the
|
232 |
-
* doctype attribute of the Document object. */
|
233 |
-
if (!isset($token['public'])) {
|
234 |
-
$token['public'] = "";
|
235 |
-
}
|
236 |
-
if (!isset($token['system'])) {
|
237 |
-
$token['system'] = "";
|
238 |
-
}
|
239 |
-
// XDOM
|
240 |
-
// Yes this is hacky. I'm kind of annoyed that I can't appendChild
|
241 |
-
// a doctype to DOMDocument. Maybe I haven't chanted the right
|
242 |
-
// syllables.
|
243 |
-
$impl = new DOMImplementation();
|
244 |
-
// This call can fail for particularly pathological cases (namely,
|
245 |
-
// the qualifiedName parameter ($token['name']) could be missing.
|
246 |
-
if ($token['name']) {
|
247 |
-
$doctype = $impl->createDocumentType($token['name'], $token['public'], $token['system']);
|
248 |
-
$this->dom->appendChild($doctype);
|
249 |
-
} else {
|
250 |
-
// It looks like libxml's not actually *able* to express this case.
|
251 |
-
// So... don't.
|
252 |
-
$this->dom->emptyDoctype = true;
|
253 |
-
}
|
254 |
-
$public = strtolower($token['public']);
|
255 |
-
$system = $token['system'] === "" ? false : strtolower($token['system']);
|
256 |
-
$publicStartsWithForQuirks = [
|
257 |
-
"+//silmaril//dtd html pro v0r11 19970101//",
|
258 |
-
"-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
|
259 |
-
"-//as//dtd html 3.0 aswedit + extensions//",
|
260 |
-
"-//ietf//dtd html 2.0 level 1//",
|
261 |
-
"-//ietf//dtd html 2.0 level 2//",
|
262 |
-
"-//ietf//dtd html 2.0 strict level 1//",
|
263 |
-
"-//ietf//dtd html 2.0 strict level 2//",
|
264 |
-
"-//ietf//dtd html 2.0 strict//",
|
265 |
-
"-//ietf//dtd html 2.0//",
|
266 |
-
"-//ietf//dtd html 2.1e//",
|
267 |
-
"-//ietf//dtd html 3.0//",
|
268 |
-
"-//ietf//dtd html 3.2 final//",
|
269 |
-
"-//ietf//dtd html 3.2//",
|
270 |
-
"-//ietf//dtd html 3//",
|
271 |
-
"-//ietf//dtd html level 0//",
|
272 |
-
"-//ietf//dtd html level 1//",
|
273 |
-
"-//ietf//dtd html level 2//",
|
274 |
-
"-//ietf//dtd html level 3//",
|
275 |
-
"-//ietf//dtd html strict level 0//",
|
276 |
-
"-//ietf//dtd html strict level 1//",
|
277 |
-
"-//ietf//dtd html strict level 2//",
|
278 |
-
"-//ietf//dtd html strict level 3//",
|
279 |
-
"-//ietf//dtd html strict//",
|
280 |
-
"-//ietf//dtd html//",
|
281 |
-
"-//metrius//dtd metrius presentational//",
|
282 |
-
"-//microsoft//dtd internet explorer 2.0 html strict//",
|
283 |
-
"-//microsoft//dtd internet explorer 2.0 html//",
|
284 |
-
"-//microsoft//dtd internet explorer 2.0 tables//",
|
285 |
-
"-//microsoft//dtd internet explorer 3.0 html strict//",
|
286 |
-
"-//microsoft//dtd internet explorer 3.0 html//",
|
287 |
-
"-//microsoft//dtd internet explorer 3.0 tables//",
|
288 |
-
"-//netscape comm. corp.//dtd html//",
|
289 |
-
"-//netscape comm. corp.//dtd strict html//",
|
290 |
-
"-//o'reilly and associates//dtd html 2.0//",
|
291 |
-
"-//o'reilly and associates//dtd html extended 1.0//",
|
292 |
-
"-//o'reilly and associates//dtd html extended relaxed 1.0//",
|
293 |
-
"-//spyglass//dtd html 2.0 extended//",
|
294 |
-
"-//sq//dtd html 2.0 hotmetal + extensions//",
|
295 |
-
"-//sun microsystems corp.//dtd hotjava html//",
|
296 |
-
"-//sun microsystems corp.//dtd hotjava strict html//",
|
297 |
-
"-//w3c//dtd html 3 1995-03-24//",
|
298 |
-
"-//w3c//dtd html 3.2 draft//",
|
299 |
-
"-//w3c//dtd html 3.2 final//",
|
300 |
-
"-//w3c//dtd html 3.2//",
|
301 |
-
"-//w3c//dtd html 3.2s draft//",
|
302 |
-
"-//w3c//dtd html 4.0 frameset//",
|
303 |
-
"-//w3c//dtd html 4.0 transitional//",
|
304 |
-
"-//w3c//dtd html experimental 19960712//",
|
305 |
-
"-//w3c//dtd html experimental 970421//",
|
306 |
-
"-//w3c//dtd w3 html//",
|
307 |
-
"-//w3o//dtd w3 html 3.0//",
|
308 |
-
"-//webtechs//dtd mozilla html 2.0//",
|
309 |
-
"-//webtechs//dtd mozilla html//",
|
310 |
-
];
|
311 |
-
$publicSetToForQuirks = [
|
312 |
-
"-//w3o//dtd w3 html strict 3.0//",
|
313 |
-
"-/w3c/dtd html 4.0 transitional/en",
|
314 |
-
"html",
|
315 |
-
];
|
316 |
-
$publicStartsWithAndSystemForQuirks = [
|
317 |
-
"-//w3c//dtd html 4.01 frameset//",
|
318 |
-
"-//w3c//dtd html 4.01 transitional//",
|
319 |
-
];
|
320 |
-
$publicStartsWithForLimitedQuirks = [
|
321 |
-
"-//w3c//dtd xhtml 1.0 frameset//",
|
322 |
-
"-//w3c//dtd xhtml 1.0 transitional//",
|
323 |
-
];
|
324 |
-
$publicStartsWithAndSystemForLimitedQuirks = [
|
325 |
-
"-//w3c//dtd html 4.01 frameset//",
|
326 |
-
"-//w3c//dtd html 4.01 transitional//",
|
327 |
-
];
|
328 |
-
// first, do easy checks
|
329 |
-
if (
|
330 |
-
!empty($token['force-quirks']) ||
|
331 |
-
strtolower($token['name']) !== 'html'
|
332 |
-
) {
|
333 |
-
$this->quirks_mode = self::QUIRKS_MODE;
|
334 |
-
} else {
|
335 |
-
do {
|
336 |
-
if ($system) {
|
337 |
-
foreach ($publicStartsWithAndSystemForQuirks as $x) {
|
338 |
-
if (strncmp($public, $x, strlen($x)) === 0) {
|
339 |
-
$this->quirks_mode = self::QUIRKS_MODE;
|
340 |
-
break;
|
341 |
-
}
|
342 |
-
}
|
343 |
-
if (!is_null($this->quirks_mode)) {
|
344 |
-
break;
|
345 |
-
}
|
346 |
-
foreach ($publicStartsWithAndSystemForLimitedQuirks as $x) {
|
347 |
-
if (strncmp($public, $x, strlen($x)) === 0) {
|
348 |
-
$this->quirks_mode = self::LIMITED_QUIRKS_MODE;
|
349 |
-
break;
|
350 |
-
}
|
351 |
-
}
|
352 |
-
if (!is_null($this->quirks_mode)) {
|
353 |
-
break;
|
354 |
-
}
|
355 |
-
}
|
356 |
-
foreach ($publicSetToForQuirks as $x) {
|
357 |
-
if ($public === $x) {
|
358 |
-
$this->quirks_mode = self::QUIRKS_MODE;
|
359 |
-
break;
|
360 |
-
}
|
361 |
-
}
|
362 |
-
if (!is_null($this->quirks_mode)) {
|
363 |
-
break;
|
364 |
-
}
|
365 |
-
foreach ($publicStartsWithForLimitedQuirks as $x) {
|
366 |
-
if (strncmp($public, $x, strlen($x)) === 0) {
|
367 |
-
$this->quirks_mode = self::LIMITED_QUIRKS_MODE;
|
368 |
-
}
|
369 |
-
}
|
370 |
-
if (!is_null($this->quirks_mode)) {
|
371 |
-
break;
|
372 |
-
}
|
373 |
-
if ($system === "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
|
374 |
-
$this->quirks_mode = self::QUIRKS_MODE;
|
375 |
-
break;
|
376 |
-
}
|
377 |
-
foreach ($publicStartsWithForQuirks as $x) {
|
378 |
-
if (strncmp($public, $x, strlen($x)) === 0) {
|
379 |
-
$this->quirks_mode = self::QUIRKS_MODE;
|
380 |
-
break;
|
381 |
-
}
|
382 |
-
}
|
383 |
-
if (is_null($this->quirks_mode)) {
|
384 |
-
$this->quirks_mode = self::NO_QUIRKS;
|
385 |
-
}
|
386 |
-
} while (false);
|
387 |
-
}
|
388 |
-
$this->mode = self::BEFORE_HTML;
|
389 |
-
} else {
|
390 |
-
// parse error
|
391 |
-
/* Switch the insertion mode to "before html", then reprocess the
|
392 |
-
* current token. */
|
393 |
-
$this->mode = self::BEFORE_HTML;
|
394 |
-
$this->quirks_mode = self::QUIRKS_MODE;
|
395 |
-
$this->emitToken($token);
|
396 |
-
}
|
397 |
-
break;
|
398 |
-
|
399 |
-
case self::BEFORE_HTML:
|
400 |
-
/* A DOCTYPE token */
|
401 |
-
if ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
402 |
-
// Parse error. Ignore the token.
|
403 |
-
$this->ignored = true;
|
404 |
-
|
405 |
-
/* A comment token */
|
406 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
407 |
-
/* Append a Comment node to the Document object with the data
|
408 |
-
attribute set to the data given in the comment token. */
|
409 |
-
// XDOM
|
410 |
-
$comment = $this->dom->createComment($token['data']);
|
411 |
-
$this->dom->appendChild($comment);
|
412 |
-
|
413 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
414 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
415 |
-
or U+0020 SPACE */
|
416 |
-
} elseif ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
417 |
-
/* Ignore the token. */
|
418 |
-
$this->ignored = true;
|
419 |
-
|
420 |
-
/* A start tag whose tag name is "html" */
|
421 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] == 'html') {
|
422 |
-
/* Create an element for the token in the HTML namespace. Append it
|
423 |
-
* to the Document object. Put this element in the stack of open
|
424 |
-
* elements. */
|
425 |
-
// XDOM
|
426 |
-
$html = $this->insertElement($token, false);
|
427 |
-
$this->dom->appendChild($html);
|
428 |
-
$this->stack[] = $html;
|
429 |
-
|
430 |
-
$this->mode = self::BEFORE_HEAD;
|
431 |
-
|
432 |
-
} else {
|
433 |
-
/* Create an html element. Append it to the Document object. Put
|
434 |
-
* this element in the stack of open elements. */
|
435 |
-
// XDOM
|
436 |
-
$html = $this->dom->createElementNS(self::NS_HTML, 'html');
|
437 |
-
$this->dom->appendChild($html);
|
438 |
-
$this->stack[] = $html;
|
439 |
-
|
440 |
-
/* Switch the insertion mode to "before head", then reprocess the
|
441 |
-
* current token. */
|
442 |
-
$this->mode = self::BEFORE_HEAD;
|
443 |
-
$this->emitToken($token);
|
444 |
-
}
|
445 |
-
break;
|
446 |
-
|
447 |
-
case self::BEFORE_HEAD:
|
448 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
449 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
450 |
-
or U+0020 SPACE */
|
451 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
452 |
-
/* Ignore the token. */
|
453 |
-
$this->ignored = true;
|
454 |
-
|
455 |
-
/* A comment token */
|
456 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
457 |
-
/* Append a Comment node to the current node with the data attribute
|
458 |
-
set to the data given in the comment token. */
|
459 |
-
$this->insertComment($token['data']);
|
460 |
-
|
461 |
-
/* A DOCTYPE token */
|
462 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
463 |
-
/* Parse error. Ignore the token */
|
464 |
-
$this->ignored = true;
|
465 |
-
// parse error
|
466 |
-
|
467 |
-
/* A start tag token with the tag name "html" */
|
468 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
469 |
-
/* Process the token using the rules for the "in body"
|
470 |
-
* insertion mode. */
|
471 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
472 |
-
|
473 |
-
/* A start tag token with the tag name "head" */
|
474 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') {
|
475 |
-
/* Insert an HTML element for the token. */
|
476 |
-
$element = $this->insertElement($token);
|
477 |
-
|
478 |
-
/* Set the head element pointer to this new element node. */
|
479 |
-
$this->head_pointer = $element;
|
480 |
-
|
481 |
-
/* Change the insertion mode to "in head". */
|
482 |
-
$this->mode = self::IN_HEAD;
|
483 |
-
|
484 |
-
/* An end tag whose tag name is one of: "head", "body", "html", "br" */
|
485 |
-
} elseif (
|
486 |
-
$token['type'] === HTML5_Tokenizer::ENDTAG && (
|
487 |
-
$token['name'] === 'head' || $token['name'] === 'body' ||
|
488 |
-
$token['name'] === 'html' || $token['name'] === 'br'
|
489 |
-
)) {
|
490 |
-
/* Act as if a start tag token with the tag name "head" and no
|
491 |
-
* attributes had been seen, then reprocess the current token. */
|
492 |
-
$this->emitToken([
|
493 |
-
'name' => 'head',
|
494 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
495 |
-
'attr' => []
|
496 |
-
]);
|
497 |
-
$this->emitToken($token);
|
498 |
-
|
499 |
-
/* Any other end tag */
|
500 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) {
|
501 |
-
/* Parse error. Ignore the token. */
|
502 |
-
$this->ignored = true;
|
503 |
-
|
504 |
-
} else {
|
505 |
-
/* Act as if a start tag token with the tag name "head" and no
|
506 |
-
* attributes had been seen, then reprocess the current token.
|
507 |
-
* Note: This will result in an empty head element being
|
508 |
-
* generated, with the current token being reprocessed in the
|
509 |
-
* "after head" insertion mode. */
|
510 |
-
$this->emitToken([
|
511 |
-
'name' => 'head',
|
512 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
513 |
-
'attr' => []
|
514 |
-
]);
|
515 |
-
$this->emitToken($token);
|
516 |
-
}
|
517 |
-
break;
|
518 |
-
|
519 |
-
case self::IN_HEAD:
|
520 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
521 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
522 |
-
or U+0020 SPACE. */
|
523 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
524 |
-
/* Insert the character into the current node. */
|
525 |
-
$this->insertText($token['data']);
|
526 |
-
|
527 |
-
/* A comment token */
|
528 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
529 |
-
/* Append a Comment node to the current node with the data attribute
|
530 |
-
set to the data given in the comment token. */
|
531 |
-
$this->insertComment($token['data']);
|
532 |
-
|
533 |
-
/* A DOCTYPE token */
|
534 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
535 |
-
/* Parse error. Ignore the token. */
|
536 |
-
$this->ignored = true;
|
537 |
-
// parse error
|
538 |
-
|
539 |
-
/* A start tag whose tag name is "html" */
|
540 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
541 |
-
$token['name'] === 'html') {
|
542 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
543 |
-
|
544 |
-
/* A start tag whose tag name is one of: "base", "command", "link" */
|
545 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
546 |
-
($token['name'] === 'base' || $token['name'] === 'command' ||
|
547 |
-
$token['name'] === 'link')) {
|
548 |
-
/* Insert an HTML element for the token. Immediately pop the
|
549 |
-
* current node off the stack of open elements. */
|
550 |
-
$this->insertElement($token);
|
551 |
-
array_pop($this->stack);
|
552 |
-
|
553 |
-
// YYY: Acknowledge the token's self-closing flag, if it is set.
|
554 |
-
|
555 |
-
/* A start tag whose tag name is "meta" */
|
556 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'meta') {
|
557 |
-
/* Insert an HTML element for the token. Immediately pop the
|
558 |
-
* current node off the stack of open elements. */
|
559 |
-
$this->insertElement($token);
|
560 |
-
array_pop($this->stack);
|
561 |
-
|
562 |
-
// XERROR: Acknowledge the token's self-closing flag, if it is set.
|
563 |
-
|
564 |
-
// XENCODING: If the element has a charset attribute, and its value is a
|
565 |
-
// supported encoding, and the confidence is currently tentative,
|
566 |
-
// then change the encoding to the encoding given by the value of
|
567 |
-
// the charset attribute.
|
568 |
-
//
|
569 |
-
// Otherwise, if the element has a content attribute, and applying
|
570 |
-
// the algorithm for extracting an encoding from a Content-Type to
|
571 |
-
// its value returns a supported encoding encoding, and the
|
572 |
-
// confidence is currently tentative, then change the encoding to
|
573 |
-
// the encoding encoding.
|
574 |
-
|
575 |
-
/* A start tag with the tag name "title" */
|
576 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'title') {
|
577 |
-
$this->insertRCDATAElement($token);
|
578 |
-
|
579 |
-
/* A start tag whose tag name is "noscript", if the scripting flag is enabled, or
|
580 |
-
* A start tag whose tag name is one of: "noframes", "style" */
|
581 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
582 |
-
($token['name'] === 'noscript' || $token['name'] === 'noframes' || $token['name'] === 'style')) {
|
583 |
-
// XSCRIPT: Scripting flag not respected
|
584 |
-
$this->insertCDATAElement($token);
|
585 |
-
|
586 |
-
// XSCRIPT: Scripting flag disable not implemented
|
587 |
-
|
588 |
-
/* A start tag with the tag name "script" */
|
589 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') {
|
590 |
-
/* 1. Create an element for the token in the HTML namespace. */
|
591 |
-
$node = $this->insertElement($token, false);
|
592 |
-
|
593 |
-
/* 2. Mark the element as being "parser-inserted" */
|
594 |
-
// Uhhh... XSCRIPT
|
595 |
-
|
596 |
-
/* 3. If the parser was originally created for the HTML
|
597 |
-
* fragment parsing algorithm, then mark the script element as
|
598 |
-
* "already executed". (fragment case) */
|
599 |
-
// ditto... XSCRIPT
|
600 |
-
|
601 |
-
/* 4. Append the new element to the current node and push it onto
|
602 |
-
* the stack of open elements. */
|
603 |
-
end($this->stack)->appendChild($node);
|
604 |
-
$this->stack[] = $node;
|
605 |
-
// I guess we could squash these together
|
606 |
-
|
607 |
-
/* 6. Let the original insertion mode be the current insertion mode. */
|
608 |
-
$this->original_mode = $this->mode;
|
609 |
-
/* 7. Switch the insertion mode to "in CDATA/RCDATA" */
|
610 |
-
$this->mode = self::IN_CDATA_RCDATA;
|
611 |
-
/* 5. Switch the tokeniser's content model flag to the CDATA state. */
|
612 |
-
$this->content_model = HTML5_Tokenizer::CDATA;
|
613 |
-
|
614 |
-
/* An end tag with the tag name "head" */
|
615 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'head') {
|
616 |
-
/* Pop the current node (which will be the head element) off the stack of open elements. */
|
617 |
-
array_pop($this->stack);
|
618 |
-
|
619 |
-
/* Change the insertion mode to "after head". */
|
620 |
-
$this->mode = self::AFTER_HEAD;
|
621 |
-
|
622 |
-
// Slight logic inversion here to minimize duplication
|
623 |
-
/* A start tag with the tag name "head". */
|
624 |
-
/* An end tag whose tag name is not one of: "body", "html", "br" */
|
625 |
-
} elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') ||
|
626 |
-
($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] !== 'html' &&
|
627 |
-
$token['name'] !== 'body' && $token['name'] !== 'br')) {
|
628 |
-
// Parse error. Ignore the token.
|
629 |
-
$this->ignored = true;
|
630 |
-
|
631 |
-
/* Anything else */
|
632 |
-
} else {
|
633 |
-
/* Act as if an end tag token with the tag name "head" had been
|
634 |
-
* seen, and reprocess the current token. */
|
635 |
-
$this->emitToken([
|
636 |
-
'name' => 'head',
|
637 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
638 |
-
]);
|
639 |
-
|
640 |
-
/* Then, reprocess the current token. */
|
641 |
-
$this->emitToken($token);
|
642 |
-
}
|
643 |
-
break;
|
644 |
-
|
645 |
-
case self::IN_HEAD_NOSCRIPT:
|
646 |
-
if ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
647 |
-
// parse error
|
648 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
649 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
650 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'noscript') {
|
651 |
-
/* Pop the current node (which will be a noscript element) from the
|
652 |
-
* stack of open elements; the new current node will be a head
|
653 |
-
* element. */
|
654 |
-
array_pop($this->stack);
|
655 |
-
$this->mode = self::IN_HEAD;
|
656 |
-
} elseif (
|
657 |
-
($token['type'] === HTML5_Tokenizer::SPACECHARACTER) ||
|
658 |
-
($token['type'] === HTML5_Tokenizer::COMMENT) ||
|
659 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG && (
|
660 |
-
$token['name'] === 'link' || $token['name'] === 'meta' ||
|
661 |
-
$token['name'] === 'noframes' || $token['name'] === 'style'))) {
|
662 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
663 |
-
// inverted logic
|
664 |
-
} elseif (
|
665 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG && (
|
666 |
-
$token['name'] === 'head' || $token['name'] === 'noscript')) ||
|
667 |
-
($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
668 |
-
$token['name'] !== 'br')) {
|
669 |
-
// parse error
|
670 |
-
} else {
|
671 |
-
// parse error
|
672 |
-
$this->emitToken([
|
673 |
-
'type' => HTML5_Tokenizer::ENDTAG,
|
674 |
-
'name' => 'noscript',
|
675 |
-
]);
|
676 |
-
$this->emitToken($token);
|
677 |
-
}
|
678 |
-
break;
|
679 |
-
|
680 |
-
case self::AFTER_HEAD:
|
681 |
-
/* Handle the token as follows: */
|
682 |
-
|
683 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
684 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
685 |
-
or U+0020 SPACE */
|
686 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
687 |
-
/* Append the character to the current node. */
|
688 |
-
$this->insertText($token['data']);
|
689 |
-
|
690 |
-
/* A comment token */
|
691 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
692 |
-
/* Append a Comment node to the current node with the data attribute
|
693 |
-
set to the data given in the comment token. */
|
694 |
-
$this->insertComment($token['data']);
|
695 |
-
|
696 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
697 |
-
// parse error
|
698 |
-
|
699 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
700 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
701 |
-
|
702 |
-
/* A start tag token with the tag name "body" */
|
703 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'body') {
|
704 |
-
$this->insertElement($token);
|
705 |
-
|
706 |
-
/* Set the frameset-ok flag to "not ok". */
|
707 |
-
$this->flag_frameset_ok = false;
|
708 |
-
|
709 |
-
/* Change the insertion mode to "in body". */
|
710 |
-
$this->mode = self::IN_BODY;
|
711 |
-
|
712 |
-
/* A start tag token with the tag name "frameset" */
|
713 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'frameset') {
|
714 |
-
/* Insert a frameset element for the token. */
|
715 |
-
$this->insertElement($token);
|
716 |
-
|
717 |
-
/* Change the insertion mode to "in frameset". */
|
718 |
-
$this->mode = self::IN_FRAMESET;
|
719 |
-
|
720 |
-
/* A start tag token whose tag name is one of: "base", "link", "meta",
|
721 |
-
"script", "style", "title" */
|
722 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
723 |
-
['base', 'link', 'meta', 'noframes', 'script', 'style', 'title'])) {
|
724 |
-
// parse error
|
725 |
-
/* Push the node pointed to by the head element pointer onto the
|
726 |
-
* stack of open elements. */
|
727 |
-
$this->stack[] = $this->head_pointer;
|
728 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
729 |
-
array_splice($this->stack, array_search($this->head_pointer, $this->stack, true), 1);
|
730 |
-
|
731 |
-
// inversion of specification
|
732 |
-
} elseif (
|
733 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') ||
|
734 |
-
($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
735 |
-
$token['name'] !== 'body' && $token['name'] !== 'html' &&
|
736 |
-
$token['name'] !== 'br')) {
|
737 |
-
// parse error
|
738 |
-
|
739 |
-
/* Anything else */
|
740 |
-
} else {
|
741 |
-
$this->emitToken([
|
742 |
-
'name' => 'body',
|
743 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
744 |
-
'attr' => []
|
745 |
-
]);
|
746 |
-
$this->flag_frameset_ok = true;
|
747 |
-
$this->emitToken($token);
|
748 |
-
}
|
749 |
-
break;
|
750 |
-
|
751 |
-
case self::IN_BODY:
|
752 |
-
/* Handle the token as follows: */
|
753 |
-
|
754 |
-
switch($token['type']) {
|
755 |
-
/* A character token */
|
756 |
-
case HTML5_Tokenizer::CHARACTER:
|
757 |
-
case HTML5_Tokenizer::SPACECHARACTER:
|
758 |
-
/* Reconstruct the active formatting elements, if any. */
|
759 |
-
$this->reconstructActiveFormattingElements();
|
760 |
-
|
761 |
-
/* Append the token's character to the current node. */
|
762 |
-
$this->insertText($token['data']);
|
763 |
-
|
764 |
-
/* If the token is not one of U+0009 CHARACTER TABULATION,
|
765 |
-
* U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020
|
766 |
-
* SPACE, then set the frameset-ok flag to "not ok". */
|
767 |
-
// i.e., if any of the characters is not whitespace
|
768 |
-
if (strlen($token['data']) !== strspn($token['data'], HTML5_Tokenizer::WHITESPACE)) {
|
769 |
-
$this->flag_frameset_ok = false;
|
770 |
-
}
|
771 |
-
break;
|
772 |
-
|
773 |
-
/* A comment token */
|
774 |
-
case HTML5_Tokenizer::COMMENT:
|
775 |
-
/* Append a Comment node to the current node with the data
|
776 |
-
attribute set to the data given in the comment token. */
|
777 |
-
$this->insertComment($token['data']);
|
778 |
-
break;
|
779 |
-
|
780 |
-
case HTML5_Tokenizer::DOCTYPE:
|
781 |
-
// parse error
|
782 |
-
break;
|
783 |
-
|
784 |
-
case HTML5_Tokenizer::EOF:
|
785 |
-
// parse error
|
786 |
-
break;
|
787 |
-
|
788 |
-
case HTML5_Tokenizer::STARTTAG:
|
789 |
-
switch($token['name']) {
|
790 |
-
case 'html':
|
791 |
-
// parse error
|
792 |
-
/* For each attribute on the token, check to see if the
|
793 |
-
* attribute is already present on the top element of the
|
794 |
-
* stack of open elements. If it is not, add the attribute
|
795 |
-
* and its corresponding value to that element. */
|
796 |
-
foreach($token['attr'] as $attr) {
|
797 |
-
if (!$this->stack[0]->hasAttribute($attr['name'])) {
|
798 |
-
$this->stack[0]->setAttribute($attr['name'], $attr['value']);
|
799 |
-
}
|
800 |
-
}
|
801 |
-
break;
|
802 |
-
|
803 |
-
case 'base': case 'command': case 'link': case 'meta': case 'noframes':
|
804 |
-
case 'script': case 'style': case 'title':
|
805 |
-
/* Process the token as if the insertion mode had been "in
|
806 |
-
head". */
|
807 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
808 |
-
break;
|
809 |
-
|
810 |
-
/* A start tag token with the tag name "body" */
|
811 |
-
case 'body':
|
812 |
-
/* Parse error. If the second element on the stack of open
|
813 |
-
elements is not a body element, or, if the stack of open
|
814 |
-
elements has only one node on it, then ignore the token.
|
815 |
-
(fragment case) */
|
816 |
-
if (count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') {
|
817 |
-
$this->ignored = true;
|
818 |
-
// Ignore
|
819 |
-
|
820 |
-
/* Otherwise, for each attribute on the token, check to see
|
821 |
-
if the attribute is already present on the body element (the
|
822 |
-
second element) on the stack of open elements. If it is not,
|
823 |
-
add the attribute and its corresponding value to that
|
824 |
-
element. */
|
825 |
-
} else {
|
826 |
-
foreach($token['attr'] as $attr) {
|
827 |
-
if (!$this->stack[1]->hasAttribute($attr['name'])) {
|
828 |
-
$this->stack[1]->setAttribute($attr['name'], $attr['value']);
|
829 |
-
}
|
830 |
-
}
|
831 |
-
}
|
832 |
-
break;
|
833 |
-
|
834 |
-
case 'frameset':
|
835 |
-
// parse error
|
836 |
-
/* If the second element on the stack of open elements is
|
837 |
-
* not a body element, or, if the stack of open elements
|
838 |
-
* has only one node on it, then ignore the token.
|
839 |
-
* (fragment case) */
|
840 |
-
if (count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') {
|
841 |
-
$this->ignored = true;
|
842 |
-
// Ignore
|
843 |
-
} elseif (!$this->flag_frameset_ok) {
|
844 |
-
$this->ignored = true;
|
845 |
-
// Ignore
|
846 |
-
} else {
|
847 |
-
/* 1. Remove the second element on the stack of open
|
848 |
-
* elements from its parent node, if it has one. */
|
849 |
-
if ($this->stack[1]->parentNode) {
|
850 |
-
$this->stack[1]->parentNode->removeChild($this->stack[1]);
|
851 |
-
}
|
852 |
-
|
853 |
-
/* 2. Pop all the nodes from the bottom of the stack of
|
854 |
-
* open elements, from the current node up to the root
|
855 |
-
* html element. */
|
856 |
-
array_splice($this->stack, 1);
|
857 |
-
|
858 |
-
$this->insertElement($token);
|
859 |
-
$this->mode = self::IN_FRAMESET;
|
860 |
-
}
|
861 |
-
break;
|
862 |
-
|
863 |
-
// in spec, there is a diversion here
|
864 |
-
|
865 |
-
case 'address': case 'article': case 'aside': case 'blockquote':
|
866 |
-
case 'center': case 'datagrid': case 'details': case 'dir':
|
867 |
-
case 'div': case 'dl': case 'fieldset': case 'figure': case 'footer':
|
868 |
-
case 'header': case 'hgroup': case 'menu': case 'nav':
|
869 |
-
case 'ol': case 'p': case 'section': case 'ul':
|
870 |
-
/* If the stack of open elements has a p element in scope,
|
871 |
-
then act as if an end tag with the tag name p had been
|
872 |
-
seen. */
|
873 |
-
if ($this->elementInScope('p')) {
|
874 |
-
$this->emitToken([
|
875 |
-
'name' => 'p',
|
876 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
877 |
-
]);
|
878 |
-
}
|
879 |
-
|
880 |
-
/* Insert an HTML element for the token. */
|
881 |
-
$this->insertElement($token);
|
882 |
-
break;
|
883 |
-
|
884 |
-
/* A start tag whose tag name is one of: "h1", "h2", "h3", "h4",
|
885 |
-
"h5", "h6" */
|
886 |
-
case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
|
887 |
-
/* If the stack of open elements has a p element in scope,
|
888 |
-
then act as if an end tag with the tag name p had been seen. */
|
889 |
-
if ($this->elementInScope('p')) {
|
890 |
-
$this->emitToken([
|
891 |
-
'name' => 'p',
|
892 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
893 |
-
]);
|
894 |
-
}
|
895 |
-
|
896 |
-
/* If the current node is an element whose tag name is one
|
897 |
-
* of "h1", "h2", "h3", "h4", "h5", or "h6", then this is a
|
898 |
-
* parse error; pop the current node off the stack of open
|
899 |
-
* elements. */
|
900 |
-
$peek = array_pop($this->stack);
|
901 |
-
if (in_array($peek->tagName, ["h1", "h2", "h3", "h4", "h5", "h6"])) {
|
902 |
-
// parse error
|
903 |
-
} else {
|
904 |
-
$this->stack[] = $peek;
|
905 |
-
}
|
906 |
-
|
907 |
-
/* Insert an HTML element for the token. */
|
908 |
-
$this->insertElement($token);
|
909 |
-
break;
|
910 |
-
|
911 |
-
case 'pre': case 'listing':
|
912 |
-
/* If the stack of open elements has a p element in scope,
|
913 |
-
then act as if an end tag with the tag name p had been seen. */
|
914 |
-
if ($this->elementInScope('p')) {
|
915 |
-
$this->emitToken([
|
916 |
-
'name' => 'p',
|
917 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
918 |
-
]);
|
919 |
-
}
|
920 |
-
$this->insertElement($token);
|
921 |
-
/* If the next token is a U+000A LINE FEED (LF) character
|
922 |
-
* token, then ignore that token and move on to the next
|
923 |
-
* one. (Newlines at the start of pre blocks are ignored as
|
924 |
-
* an authoring convenience.) */
|
925 |
-
$this->ignore_lf_token = 2;
|
926 |
-
$this->flag_frameset_ok = false;
|
927 |
-
break;
|
928 |
-
|
929 |
-
/* A start tag whose tag name is "form" */
|
930 |
-
case 'form':
|
931 |
-
/* If the form element pointer is not null, ignore the
|
932 |
-
token with a parse error. */
|
933 |
-
if ($this->form_pointer !== null) {
|
934 |
-
$this->ignored = true;
|
935 |
-
// Ignore.
|
936 |
-
|
937 |
-
/* Otherwise: */
|
938 |
-
} else {
|
939 |
-
/* If the stack of open elements has a p element in
|
940 |
-
scope, then act as if an end tag with the tag name p
|
941 |
-
had been seen. */
|
942 |
-
if ($this->elementInScope('p')) {
|
943 |
-
$this->emitToken([
|
944 |
-
'name' => 'p',
|
945 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
946 |
-
]);
|
947 |
-
}
|
948 |
-
|
949 |
-
/* Insert an HTML element for the token, and set the
|
950 |
-
form element pointer to point to the element created. */
|
951 |
-
$element = $this->insertElement($token);
|
952 |
-
$this->form_pointer = $element;
|
953 |
-
}
|
954 |
-
break;
|
955 |
-
|
956 |
-
// condensed specification
|
957 |
-
case 'li': case 'dc': case 'dd': case 'ds': case 'dt':
|
958 |
-
/* 1. Set the frameset-ok flag to "not ok". */
|
959 |
-
$this->flag_frameset_ok = false;
|
960 |
-
|
961 |
-
$stack_length = count($this->stack) - 1;
|
962 |
-
for($n = $stack_length; 0 <= $n; $n--) {
|
963 |
-
/* 2. Initialise node to be the current node (the
|
964 |
-
bottommost node of the stack). */
|
965 |
-
$stop = false;
|
966 |
-
$node = $this->stack[$n];
|
967 |
-
$cat = $this->getElementCategory($node);
|
968 |
-
|
969 |
-
// for case 'li':
|
970 |
-
/* 3. If node is an li element, then act as if an end
|
971 |
-
* tag with the tag name "li" had been seen, then jump
|
972 |
-
* to the last step. */
|
973 |
-
// for case 'dc': case 'dd': case 'ds': case 'dt':
|
974 |
-
/* If node is a dc, dd, ds or dt element, then act as if an end
|
975 |
-
* tag with the same tag name as node had been seen, then
|
976 |
-
* jump to the last step. */
|
977 |
-
if (($token['name'] === 'li' && $node->tagName === 'li') ||
|
978 |
-
($token['name'] !== 'li' && ($node->tagName == 'dc' || $node->tagName === 'dd' || $node->tagName == 'ds' || $node->tagName === 'dt'))) { // limited conditional
|
979 |
-
$this->emitToken([
|
980 |
-
'type' => HTML5_Tokenizer::ENDTAG,
|
981 |
-
'name' => $node->tagName,
|
982 |
-
]);
|
983 |
-
break;
|
984 |
-
}
|
985 |
-
|
986 |
-
/* 4. If node is not in the formatting category, and is
|
987 |
-
not in the phrasing category, and is not an address,
|
988 |
-
div or p element, then stop this algorithm. */
|
989 |
-
if ($cat !== self::FORMATTING && $cat !== self::PHRASING &&
|
990 |
-
$node->tagName !== 'address' && $node->tagName !== 'div' &&
|
991 |
-
$node->tagName !== 'p') {
|
992 |
-
break;
|
993 |
-
}
|
994 |
-
|
995 |
-
/* 5. Otherwise, set node to the previous entry in the
|
996 |
-
* stack of open elements and return to step 2. */
|
997 |
-
}
|
998 |
-
|
999 |
-
/* 6. This is the last step. */
|
1000 |
-
|
1001 |
-
/* If the stack of open elements has a p element in scope,
|
1002 |
-
then act as if an end tag with the tag name p had been
|
1003 |
-
seen. */
|
1004 |
-
if ($this->elementInScope('p')) {
|
1005 |
-
$this->emitToken([
|
1006 |
-
'name' => 'p',
|
1007 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1008 |
-
]);
|
1009 |
-
}
|
1010 |
-
|
1011 |
-
/* Finally, insert an HTML element with the same tag
|
1012 |
-
name as the token's. */
|
1013 |
-
$this->insertElement($token);
|
1014 |
-
break;
|
1015 |
-
|
1016 |
-
/* A start tag token whose tag name is "plaintext" */
|
1017 |
-
case 'plaintext':
|
1018 |
-
/* If the stack of open elements has a p element in scope,
|
1019 |
-
then act as if an end tag with the tag name p had been
|
1020 |
-
seen. */
|
1021 |
-
if ($this->elementInScope('p')) {
|
1022 |
-
$this->emitToken([
|
1023 |
-
'name' => 'p',
|
1024 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1025 |
-
]);
|
1026 |
-
}
|
1027 |
-
|
1028 |
-
/* Insert an HTML element for the token. */
|
1029 |
-
$this->insertElement($token);
|
1030 |
-
|
1031 |
-
$this->content_model = HTML5_Tokenizer::PLAINTEXT;
|
1032 |
-
break;
|
1033 |
-
|
1034 |
-
// more diversions
|
1035 |
-
|
1036 |
-
/* A start tag whose tag name is "a" */
|
1037 |
-
case 'a':
|
1038 |
-
/* If the list of active formatting elements contains
|
1039 |
-
an element whose tag name is "a" between the end of the
|
1040 |
-
list and the last marker on the list (or the start of
|
1041 |
-
the list if there is no marker on the list), then this
|
1042 |
-
is a parse error; act as if an end tag with the tag name
|
1043 |
-
"a" had been seen, then remove that element from the list
|
1044 |
-
of active formatting elements and the stack of open
|
1045 |
-
elements if the end tag didn't already remove it (it
|
1046 |
-
might not have if the element is not in table scope). */
|
1047 |
-
$leng = count($this->a_formatting);
|
1048 |
-
|
1049 |
-
for ($n = $leng - 1; $n >= 0; $n--) {
|
1050 |
-
if ($this->a_formatting[$n] === self::MARKER) {
|
1051 |
-
break;
|
1052 |
-
|
1053 |
-
} elseif ($this->a_formatting[$n]->tagName === 'a') {
|
1054 |
-
$a = $this->a_formatting[$n];
|
1055 |
-
$this->emitToken([
|
1056 |
-
'name' => 'a',
|
1057 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1058 |
-
]);
|
1059 |
-
if (in_array($a, $this->a_formatting)) {
|
1060 |
-
$a_i = array_search($a, $this->a_formatting, true);
|
1061 |
-
if ($a_i !== false) {
|
1062 |
-
array_splice($this->a_formatting, $a_i, 1);
|
1063 |
-
}
|
1064 |
-
}
|
1065 |
-
if (in_array($a, $this->stack)) {
|
1066 |
-
$a_i = array_search($a, $this->stack, true);
|
1067 |
-
if ($a_i !== false) {
|
1068 |
-
array_splice($this->stack, $a_i, 1);
|
1069 |
-
}
|
1070 |
-
}
|
1071 |
-
break;
|
1072 |
-
}
|
1073 |
-
}
|
1074 |
-
|
1075 |
-
/* Reconstruct the active formatting elements, if any. */
|
1076 |
-
$this->reconstructActiveFormattingElements();
|
1077 |
-
|
1078 |
-
/* Insert an HTML element for the token. */
|
1079 |
-
$el = $this->insertElement($token);
|
1080 |
-
|
1081 |
-
/* Add that element to the list of active formatting
|
1082 |
-
elements. */
|
1083 |
-
$this->a_formatting[] = $el;
|
1084 |
-
break;
|
1085 |
-
|
1086 |
-
case 'b': case 'big': case 'code': case 'em': case 'font': case 'i':
|
1087 |
-
case 's': case 'small': case 'strike':
|
1088 |
-
case 'strong': case 'tt': case 'u':
|
1089 |
-
/* Reconstruct the active formatting elements, if any. */
|
1090 |
-
$this->reconstructActiveFormattingElements();
|
1091 |
-
|
1092 |
-
/* Insert an HTML element for the token. */
|
1093 |
-
$el = $this->insertElement($token);
|
1094 |
-
|
1095 |
-
/* Add that element to the list of active formatting
|
1096 |
-
elements. */
|
1097 |
-
$this->a_formatting[] = $el;
|
1098 |
-
break;
|
1099 |
-
|
1100 |
-
case 'nobr':
|
1101 |
-
/* Reconstruct the active formatting elements, if any. */
|
1102 |
-
$this->reconstructActiveFormattingElements();
|
1103 |
-
|
1104 |
-
/* If the stack of open elements has a nobr element in
|
1105 |
-
* scope, then this is a parse error; act as if an end tag
|
1106 |
-
* with the tag name "nobr" had been seen, then once again
|
1107 |
-
* reconstruct the active formatting elements, if any. */
|
1108 |
-
if ($this->elementInScope('nobr')) {
|
1109 |
-
$this->emitToken([
|
1110 |
-
'name' => 'nobr',
|
1111 |
-
'type' => HTML5_Tokenizer::ENDTAG,
|
1112 |
-
]);
|
1113 |
-
$this->reconstructActiveFormattingElements();
|
1114 |
-
}
|
1115 |
-
|
1116 |
-
/* Insert an HTML element for the token. */
|
1117 |
-
$el = $this->insertElement($token);
|
1118 |
-
|
1119 |
-
/* Add that element to the list of active formatting
|
1120 |
-
elements. */
|
1121 |
-
$this->a_formatting[] = $el;
|
1122 |
-
break;
|
1123 |
-
|
1124 |
-
// another diversion
|
1125 |
-
|
1126 |
-
/* A start tag token whose tag name is "button" */
|
1127 |
-
case 'button':
|
1128 |
-
/* If the stack of open elements has a button element in scope,
|
1129 |
-
then this is a parse error; act as if an end tag with the tag
|
1130 |
-
name "button" had been seen, then reprocess the token. (We don't
|
1131 |
-
do that. Unnecessary.) (I hope you're right! -- ezyang) */
|
1132 |
-
if ($this->elementInScope('button')) {
|
1133 |
-
$this->emitToken([
|
1134 |
-
'name' => 'button',
|
1135 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1136 |
-
]);
|
1137 |
-
}
|
1138 |
-
|
1139 |
-
/* Reconstruct the active formatting elements, if any. */
|
1140 |
-
$this->reconstructActiveFormattingElements();
|
1141 |
-
|
1142 |
-
/* Insert an HTML element for the token. */
|
1143 |
-
$this->insertElement($token);
|
1144 |
-
|
1145 |
-
/* Insert a marker at the end of the list of active
|
1146 |
-
formatting elements. */
|
1147 |
-
$this->a_formatting[] = self::MARKER;
|
1148 |
-
|
1149 |
-
$this->flag_frameset_ok = false;
|
1150 |
-
break;
|
1151 |
-
|
1152 |
-
case 'applet': case 'marquee': case 'object':
|
1153 |
-
/* Reconstruct the active formatting elements, if any. */
|
1154 |
-
$this->reconstructActiveFormattingElements();
|
1155 |
-
|
1156 |
-
/* Insert an HTML element for the token. */
|
1157 |
-
$this->insertElement($token);
|
1158 |
-
|
1159 |
-
/* Insert a marker at the end of the list of active
|
1160 |
-
formatting elements. */
|
1161 |
-
$this->a_formatting[] = self::MARKER;
|
1162 |
-
|
1163 |
-
$this->flag_frameset_ok = false;
|
1164 |
-
break;
|
1165 |
-
|
1166 |
-
// spec diversion
|
1167 |
-
|
1168 |
-
/* A start tag whose tag name is "table" */
|
1169 |
-
case 'table':
|
1170 |
-
/* If the Document is not set to quirks mode, and the
|
1171 |
-
* stack of open elements has a p element in scope, then
|
1172 |
-
* act as if an end tag with the tag name "p" had been
|
1173 |
-
* seen. */
|
1174 |
-
if ($this->quirks_mode !== self::QUIRKS_MODE &&
|
1175 |
-
$this->elementInScope('p')) {
|
1176 |
-
$this->emitToken([
|
1177 |
-
'name' => 'p',
|
1178 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1179 |
-
]);
|
1180 |
-
}
|
1181 |
-
|
1182 |
-
/* Insert an HTML element for the token. */
|
1183 |
-
$this->insertElement($token);
|
1184 |
-
|
1185 |
-
$this->flag_frameset_ok = false;
|
1186 |
-
|
1187 |
-
/* Change the insertion mode to "in table". */
|
1188 |
-
$this->mode = self::IN_TABLE;
|
1189 |
-
break;
|
1190 |
-
|
1191 |
-
/* A start tag whose tag name is one of: "area", "basefont",
|
1192 |
-
"bgsound", "br", "embed", "img", "param", "spacer", "wbr" */
|
1193 |
-
case 'area': case 'basefont': case 'bgsound': case 'br':
|
1194 |
-
case 'embed': case 'img': case 'input': case 'keygen': case 'spacer':
|
1195 |
-
case 'wbr':
|
1196 |
-
/* Reconstruct the active formatting elements, if any. */
|
1197 |
-
$this->reconstructActiveFormattingElements();
|
1198 |
-
|
1199 |
-
/* Insert an HTML element for the token. */
|
1200 |
-
$this->insertElement($token);
|
1201 |
-
|
1202 |
-
/* Immediately pop the current node off the stack of open elements. */
|
1203 |
-
array_pop($this->stack);
|
1204 |
-
|
1205 |
-
// YYY: Acknowledge the token's self-closing flag, if it is set.
|
1206 |
-
|
1207 |
-
$this->flag_frameset_ok = false;
|
1208 |
-
break;
|
1209 |
-
|
1210 |
-
case 'param': case 'source':
|
1211 |
-
/* Insert an HTML element for the token. */
|
1212 |
-
$this->insertElement($token);
|
1213 |
-
|
1214 |
-
/* Immediately pop the current node off the stack of open elements. */
|
1215 |
-
array_pop($this->stack);
|
1216 |
-
|
1217 |
-
// YYY: Acknowledge the token's self-closing flag, if it is set.
|
1218 |
-
break;
|
1219 |
-
|
1220 |
-
/* A start tag whose tag name is "hr" */
|
1221 |
-
case 'hr':
|
1222 |
-
/* If the stack of open elements has a p element in scope,
|
1223 |
-
then act as if an end tag with the tag name p had been seen. */
|
1224 |
-
if ($this->elementInScope('p')) {
|
1225 |
-
$this->emitToken([
|
1226 |
-
'name' => 'p',
|
1227 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1228 |
-
]);
|
1229 |
-
}
|
1230 |
-
|
1231 |
-
/* Insert an HTML element for the token. */
|
1232 |
-
$this->insertElement($token);
|
1233 |
-
|
1234 |
-
/* Immediately pop the current node off the stack of open elements. */
|
1235 |
-
array_pop($this->stack);
|
1236 |
-
|
1237 |
-
// YYY: Acknowledge the token's self-closing flag, if it is set.
|
1238 |
-
|
1239 |
-
$this->flag_frameset_ok = false;
|
1240 |
-
break;
|
1241 |
-
|
1242 |
-
/* A start tag whose tag name is "image" */
|
1243 |
-
case 'image':
|
1244 |
-
/* Parse error. Change the token's tag name to "img" and
|
1245 |
-
reprocess it. (Don't ask.) */
|
1246 |
-
$token['name'] = 'img';
|
1247 |
-
$this->emitToken($token);
|
1248 |
-
break;
|
1249 |
-
|
1250 |
-
/* A start tag whose tag name is "isindex" */
|
1251 |
-
case 'isindex':
|
1252 |
-
/* Parse error. */
|
1253 |
-
|
1254 |
-
/* If the form element pointer is not null,
|
1255 |
-
then ignore the token. */
|
1256 |
-
if ($this->form_pointer === null) {
|
1257 |
-
/* Act as if a start tag token with the tag name "form" had
|
1258 |
-
been seen. */
|
1259 |
-
/* If the token has an attribute called "action", set
|
1260 |
-
* the action attribute on the resulting form
|
1261 |
-
* element to the value of the "action" attribute of
|
1262 |
-
* the token. */
|
1263 |
-
$attr = [];
|
1264 |
-
$action = $this->getAttr($token, 'action');
|
1265 |
-
if ($action !== false) {
|
1266 |
-
$attr[] = ['name' => 'action', 'value' => $action];
|
1267 |
-
}
|
1268 |
-
$this->emitToken([
|
1269 |
-
'name' => 'form',
|
1270 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
1271 |
-
'attr' => $attr
|
1272 |
-
]);
|
1273 |
-
|
1274 |
-
/* Act as if a start tag token with the tag name "hr" had
|
1275 |
-
been seen. */
|
1276 |
-
$this->emitToken([
|
1277 |
-
'name' => 'hr',
|
1278 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
1279 |
-
'attr' => []
|
1280 |
-
]);
|
1281 |
-
|
1282 |
-
/* Act as if a start tag token with the tag name "label"
|
1283 |
-
had been seen. */
|
1284 |
-
$this->emitToken([
|
1285 |
-
'name' => 'label',
|
1286 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
1287 |
-
'attr' => []
|
1288 |
-
]);
|
1289 |
-
|
1290 |
-
/* Act as if a stream of character tokens had been seen. */
|
1291 |
-
$prompt = $this->getAttr($token, 'prompt');
|
1292 |
-
if ($prompt === false) {
|
1293 |
-
$prompt = 'This is a searchable index. '.
|
1294 |
-
'Insert your search keywords here: ';
|
1295 |
-
}
|
1296 |
-
$this->emitToken([
|
1297 |
-
'data' => $prompt,
|
1298 |
-
'type' => HTML5_Tokenizer::CHARACTER,
|
1299 |
-
]);
|
1300 |
-
|
1301 |
-
/* Act as if a start tag token with the tag name "input"
|
1302 |
-
had been seen, with all the attributes from the "isindex"
|
1303 |
-
token, except with the "name" attribute set to the value
|
1304 |
-
"isindex" (ignoring any explicit "name" attribute). */
|
1305 |
-
$attr = [];
|
1306 |
-
foreach ($token['attr'] as $keypair) {
|
1307 |
-
if ($keypair['name'] === 'name' || $keypair['name'] === 'action' ||
|
1308 |
-
$keypair['name'] === 'prompt') {
|
1309 |
-
continue;
|
1310 |
-
}
|
1311 |
-
$attr[] = $keypair;
|
1312 |
-
}
|
1313 |
-
$attr[] = ['name' => 'name', 'value' => 'isindex'];
|
1314 |
-
|
1315 |
-
$this->emitToken([
|
1316 |
-
'name' => 'input',
|
1317 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
1318 |
-
'attr' => $attr
|
1319 |
-
]);
|
1320 |
-
|
1321 |
-
/* Act as if an end tag token with the tag name "label"
|
1322 |
-
had been seen. */
|
1323 |
-
$this->emitToken([
|
1324 |
-
'name' => 'label',
|
1325 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1326 |
-
]);
|
1327 |
-
|
1328 |
-
/* Act as if a start tag token with the tag name "hr" had
|
1329 |
-
been seen. */
|
1330 |
-
$this->emitToken([
|
1331 |
-
'name' => 'hr',
|
1332 |
-
'type' => HTML5_Tokenizer::STARTTAG
|
1333 |
-
]);
|
1334 |
-
|
1335 |
-
/* Act as if an end tag token with the tag name "form" had
|
1336 |
-
been seen. */
|
1337 |
-
$this->emitToken([
|
1338 |
-
'name' => 'form',
|
1339 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1340 |
-
]);
|
1341 |
-
} else {
|
1342 |
-
$this->ignored = true;
|
1343 |
-
}
|
1344 |
-
break;
|
1345 |
-
|
1346 |
-
/* A start tag whose tag name is "textarea" */
|
1347 |
-
case 'textarea':
|
1348 |
-
$this->insertElement($token);
|
1349 |
-
|
1350 |
-
/* If the next token is a U+000A LINE FEED (LF)
|
1351 |
-
* character token, then ignore that token and move on to
|
1352 |
-
* the next one. (Newlines at the start of textarea
|
1353 |
-
* elements are ignored as an authoring convenience.)
|
1354 |
-
* need flag, see also <pre> */
|
1355 |
-
$this->ignore_lf_token = 2;
|
1356 |
-
|
1357 |
-
$this->original_mode = $this->mode;
|
1358 |
-
$this->flag_frameset_ok = false;
|
1359 |
-
$this->mode = self::IN_CDATA_RCDATA;
|
1360 |
-
|
1361 |
-
/* Switch the tokeniser's content model flag to the
|
1362 |
-
RCDATA state. */
|
1363 |
-
$this->content_model = HTML5_Tokenizer::RCDATA;
|
1364 |
-
break;
|
1365 |
-
|
1366 |
-
/* A start tag token whose tag name is "xmp" */
|
1367 |
-
case 'xmp':
|
1368 |
-
/* If the stack of open elements has a p element in
|
1369 |
-
scope, then act as if an end tag with the tag name
|
1370 |
-
"p" has been seen. */
|
1371 |
-
if ($this->elementInScope('p')) {
|
1372 |
-
$this->emitToken([
|
1373 |
-
'name' => 'p',
|
1374 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1375 |
-
]);
|
1376 |
-
}
|
1377 |
-
|
1378 |
-
/* Reconstruct the active formatting elements, if any. */
|
1379 |
-
$this->reconstructActiveFormattingElements();
|
1380 |
-
|
1381 |
-
$this->flag_frameset_ok = false;
|
1382 |
-
|
1383 |
-
$this->insertCDATAElement($token);
|
1384 |
-
break;
|
1385 |
-
|
1386 |
-
case 'iframe':
|
1387 |
-
$this->flag_frameset_ok = false;
|
1388 |
-
$this->insertCDATAElement($token);
|
1389 |
-
break;
|
1390 |
-
|
1391 |
-
case 'noembed': case 'noscript':
|
1392 |
-
// XSCRIPT: should check scripting flag
|
1393 |
-
$this->insertCDATAElement($token);
|
1394 |
-
break;
|
1395 |
-
|
1396 |
-
/* A start tag whose tag name is "select" */
|
1397 |
-
case 'select':
|
1398 |
-
/* Reconstruct the active formatting elements, if any. */
|
1399 |
-
$this->reconstructActiveFormattingElements();
|
1400 |
-
|
1401 |
-
/* Insert an HTML element for the token. */
|
1402 |
-
$this->insertElement($token);
|
1403 |
-
|
1404 |
-
$this->flag_frameset_ok = false;
|
1405 |
-
|
1406 |
-
/* If the insertion mode is one of in table", "in caption",
|
1407 |
-
* "in column group", "in table body", "in row", or "in
|
1408 |
-
* cell", then switch the insertion mode to "in select in
|
1409 |
-
* table". Otherwise, switch the insertion mode to "in
|
1410 |
-
* select". */
|
1411 |
-
if (
|
1412 |
-
$this->mode === self::IN_TABLE || $this->mode === self::IN_CAPTION ||
|
1413 |
-
$this->mode === self::IN_COLUMN_GROUP || $this->mode ==+self::IN_TABLE_BODY ||
|
1414 |
-
$this->mode === self::IN_ROW || $this->mode === self::IN_CELL
|
1415 |
-
) {
|
1416 |
-
$this->mode = self::IN_SELECT_IN_TABLE;
|
1417 |
-
} else {
|
1418 |
-
$this->mode = self::IN_SELECT;
|
1419 |
-
}
|
1420 |
-
break;
|
1421 |
-
|
1422 |
-
case 'option': case 'optgroup':
|
1423 |
-
if ($this->elementInScope('option')) {
|
1424 |
-
$this->emitToken([
|
1425 |
-
'name' => 'option',
|
1426 |
-
'type' => HTML5_Tokenizer::ENDTAG,
|
1427 |
-
]);
|
1428 |
-
}
|
1429 |
-
$this->reconstructActiveFormattingElements();
|
1430 |
-
$this->insertElement($token);
|
1431 |
-
break;
|
1432 |
-
|
1433 |
-
case 'rp': case 'rt':
|
1434 |
-
/* If the stack of open elements has a ruby element in scope, then generate
|
1435 |
-
* implied end tags. If the current node is not then a ruby element, this is
|
1436 |
-
* a parse error; pop all the nodes from the current node up to the node
|
1437 |
-
* immediately before the bottommost ruby element on the stack of open elements.
|
1438 |
-
*/
|
1439 |
-
if ($this->elementInScope('ruby')) {
|
1440 |
-
$this->generateImpliedEndTags();
|
1441 |
-
}
|
1442 |
-
$peek = false;
|
1443 |
-
do {
|
1444 |
-
/*if ($peek) {
|
1445 |
-
// parse error
|
1446 |
-
}*/
|
1447 |
-
$peek = array_pop($this->stack);
|
1448 |
-
} while ($peek->tagName !== 'ruby');
|
1449 |
-
$this->stack[] = $peek; // we popped one too many
|
1450 |
-
$this->insertElement($token);
|
1451 |
-
break;
|
1452 |
-
|
1453 |
-
// spec diversion
|
1454 |
-
|
1455 |
-
case 'math':
|
1456 |
-
$this->reconstructActiveFormattingElements();
|
1457 |
-
$token = $this->adjustMathMLAttributes($token);
|
1458 |
-
$token = $this->adjustForeignAttributes($token);
|
1459 |
-
$this->insertForeignElement($token, self::NS_MATHML);
|
1460 |
-
if (isset($token['self-closing'])) {
|
1461 |
-
// XERROR: acknowledge the token's self-closing flag
|
1462 |
-
array_pop($this->stack);
|
1463 |
-
}
|
1464 |
-
if ($this->mode !== self::IN_FOREIGN_CONTENT) {
|
1465 |
-
$this->secondary_mode = $this->mode;
|
1466 |
-
$this->mode = self::IN_FOREIGN_CONTENT;
|
1467 |
-
}
|
1468 |
-
break;
|
1469 |
-
|
1470 |
-
case 'svg':
|
1471 |
-
$this->reconstructActiveFormattingElements();
|
1472 |
-
$token = $this->adjustSVGAttributes($token);
|
1473 |
-
$token = $this->adjustForeignAttributes($token);
|
1474 |
-
$this->insertForeignElement($token, self::NS_SVG);
|
1475 |
-
if (isset($token['self-closing'])) {
|
1476 |
-
// XERROR: acknowledge the token's self-closing flag
|
1477 |
-
array_pop($this->stack);
|
1478 |
-
}
|
1479 |
-
if ($this->mode !== self::IN_FOREIGN_CONTENT) {
|
1480 |
-
$this->secondary_mode = $this->mode;
|
1481 |
-
$this->mode = self::IN_FOREIGN_CONTENT;
|
1482 |
-
}
|
1483 |
-
break;
|
1484 |
-
|
1485 |
-
case 'caption': case 'col': case 'colgroup': case 'frame': case 'head':
|
1486 |
-
case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead': case 'tr':
|
1487 |
-
// parse error
|
1488 |
-
break;
|
1489 |
-
|
1490 |
-
/* A start tag token not covered by the previous entries */
|
1491 |
-
default:
|
1492 |
-
/* Reconstruct the active formatting elements, if any. */
|
1493 |
-
$this->reconstructActiveFormattingElements();
|
1494 |
-
|
1495 |
-
$this->insertElement($token);
|
1496 |
-
/* This element will be a phrasing element. */
|
1497 |
-
break;
|
1498 |
-
}
|
1499 |
-
break;
|
1500 |
-
|
1501 |
-
case HTML5_Tokenizer::ENDTAG:
|
1502 |
-
switch ($token['name']) {
|
1503 |
-
/* An end tag with the tag name "body" */
|
1504 |
-
case 'body':
|
1505 |
-
/* If the stack of open elements does not have a body
|
1506 |
-
* element in scope, this is a parse error; ignore the
|
1507 |
-
* token. */
|
1508 |
-
if (!$this->elementInScope('body')) {
|
1509 |
-
$this->ignored = true;
|
1510 |
-
|
1511 |
-
/* Otherwise, if there is a node in the stack of open
|
1512 |
-
* elements that is not either a dc element, a dd element,
|
1513 |
-
* a ds element, a dt element, an li element, an optgroup
|
1514 |
-
* element, an option element, a p element, an rp element,
|
1515 |
-
* an rt element, a tbody element, a td element, a tfoot
|
1516 |
-
* element, a th element, a thead element, a tr element,
|
1517 |
-
* the body element, or the html element, then this is a
|
1518 |
-
* parse error.
|
1519 |
-
*/
|
1520 |
-
} else {
|
1521 |
-
// XERROR: implement this check for parse error
|
1522 |
-
}
|
1523 |
-
|
1524 |
-
/* Change the insertion mode to "after body". */
|
1525 |
-
$this->mode = self::AFTER_BODY;
|
1526 |
-
break;
|
1527 |
-
|
1528 |
-
/* An end tag with the tag name "html" */
|
1529 |
-
case 'html':
|
1530 |
-
/* Act as if an end tag with tag name "body" had been seen,
|
1531 |
-
then, if that token wasn't ignored, reprocess the current
|
1532 |
-
token. */
|
1533 |
-
$this->emitToken([
|
1534 |
-
'name' => 'body',
|
1535 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
1536 |
-
]);
|
1537 |
-
|
1538 |
-
if (!$this->ignored) {
|
1539 |
-
$this->emitToken($token);
|
1540 |
-
}
|
1541 |
-
break;
|
1542 |
-
|
1543 |
-
case 'address': case 'article': case 'aside': case 'blockquote':
|
1544 |
-
case 'center': case 'datagrid': case 'details': case 'dir':
|
1545 |
-
case 'div': case 'dl': case 'fieldset': case 'footer':
|
1546 |
-
case 'header': case 'hgroup': case 'listing': case 'menu':
|
1547 |
-
case 'nav': case 'ol': case 'pre': case 'section': case 'ul':
|
1548 |
-
/* If the stack of open elements has an element in scope
|
1549 |
-
with the same tag name as that of the token, then generate
|
1550 |
-
implied end tags. */
|
1551 |
-
if ($this->elementInScope($token['name'])) {
|
1552 |
-
$this->generateImpliedEndTags();
|
1553 |
-
|
1554 |
-
/* Now, if the current node is not an element with
|
1555 |
-
the same tag name as that of the token, then this
|
1556 |
-
is a parse error. */
|
1557 |
-
// XERROR: implement parse error logic
|
1558 |
-
|
1559 |
-
/* If the stack of open elements has an element in
|
1560 |
-
scope with the same tag name as that of the token,
|
1561 |
-
then pop elements from this stack until an element
|
1562 |
-
with that tag name has been popped from the stack. */
|
1563 |
-
do {
|
1564 |
-
$node = array_pop($this->stack);
|
1565 |
-
} while ($node->tagName !== $token['name']);
|
1566 |
-
} else {
|
1567 |
-
// parse error
|
1568 |
-
}
|
1569 |
-
break;
|
1570 |
-
|
1571 |
-
/* An end tag whose tag name is "form" */
|
1572 |
-
case 'form':
|
1573 |
-
/* Let node be the element that the form element pointer is set to. */
|
1574 |
-
$node = $this->form_pointer;
|
1575 |
-
/* Set the form element pointer to null. */
|
1576 |
-
$this->form_pointer = null;
|
1577 |
-
/* If node is null or the stack of open elements does not
|
1578 |
-
* have node in scope, then this is a parse error; ignore the token. */
|
1579 |
-
if ($node === null || !in_array($node, $this->stack)) {
|
1580 |
-
// parse error
|
1581 |
-
$this->ignored = true;
|
1582 |
-
} else {
|
1583 |
-
/* 1. Generate implied end tags. */
|
1584 |
-
$this->generateImpliedEndTags();
|
1585 |
-
/* 2. If the current node is not node, then this is a parse error. */
|
1586 |
-
if (end($this->stack) !== $node) {
|
1587 |
-
// parse error
|
1588 |
-
}
|
1589 |
-
/* 3. Remove node from the stack of open elements. */
|
1590 |
-
array_splice($this->stack, array_search($node, $this->stack, true), 1);
|
1591 |
-
}
|
1592 |
-
|
1593 |
-
break;
|
1594 |
-
|
1595 |
-
/* An end tag whose tag name is "p" */
|
1596 |
-
case 'p':
|
1597 |
-
/* If the stack of open elements has a p element in scope,
|
1598 |
-
then generate implied end tags, except for p elements. */
|
1599 |
-
if ($this->elementInScope('p')) {
|
1600 |
-
/* Generate implied end tags, except for elements with
|
1601 |
-
* the same tag name as the token. */
|
1602 |
-
$this->generateImpliedEndTags(['p']);
|
1603 |
-
|
1604 |
-
/* If the current node is not a p element, then this is
|
1605 |
-
a parse error. */
|
1606 |
-
// XERROR: implement
|
1607 |
-
|
1608 |
-
/* Pop elements from the stack of open elements until
|
1609 |
-
* an element with the same tag name as the token has
|
1610 |
-
* been popped from the stack. */
|
1611 |
-
do {
|
1612 |
-
$node = array_pop($this->stack);
|
1613 |
-
} while ($node->tagName !== 'p');
|
1614 |
-
|
1615 |
-
} else {
|
1616 |
-
// parse error
|
1617 |
-
$this->emitToken([
|
1618 |
-
'name' => 'p',
|
1619 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
1620 |
-
]);
|
1621 |
-
$this->emitToken($token);
|
1622 |
-
}
|
1623 |
-
break;
|
1624 |
-
|
1625 |
-
/* An end tag whose tag name is "li" */
|
1626 |
-
case 'li':
|
1627 |
-
/* If the stack of open elements does not have an element
|
1628 |
-
* in list item scope with the same tag name as that of the
|
1629 |
-
* token, then this is a parse error; ignore the token. */
|
1630 |
-
if ($this->elementInScope($token['name'], self::SCOPE_LISTITEM)) {
|
1631 |
-
/* Generate implied end tags, except for elements with the
|
1632 |
-
* same tag name as the token. */
|
1633 |
-
$this->generateImpliedEndTags([$token['name']]);
|
1634 |
-
/* If the current node is not an element with the same tag
|
1635 |
-
* name as that of the token, then this is a parse error. */
|
1636 |
-
// XERROR: parse error
|
1637 |
-
/* Pop elements from the stack of open elements until an
|
1638 |
-
* element with the same tag name as the token has been
|
1639 |
-
* popped from the stack. */
|
1640 |
-
do {
|
1641 |
-
$node = array_pop($this->stack);
|
1642 |
-
} while ($node->tagName !== $token['name']);
|
1643 |
-
}
|
1644 |
-
/*else {
|
1645 |
-
// XERROR: parse error
|
1646 |
-
}*/
|
1647 |
-
break;
|
1648 |
-
|
1649 |
-
/* An end tag whose tag name is "dc", "dd", "ds", "dt" */
|
1650 |
-
case 'dc': case 'dd': case 'ds': case 'dt':
|
1651 |
-
if ($this->elementInScope($token['name'])) {
|
1652 |
-
$this->generateImpliedEndTags([$token['name']]);
|
1653 |
-
|
1654 |
-
/* If the current node is not an element with the same
|
1655 |
-
tag name as the token, then this is a parse error. */
|
1656 |
-
// XERROR: implement parse error
|
1657 |
-
|
1658 |
-
/* Pop elements from the stack of open elements until
|
1659 |
-
* an element with the same tag name as the token has
|
1660 |
-
* been popped from the stack. */
|
1661 |
-
do {
|
1662 |
-
$node = array_pop($this->stack);
|
1663 |
-
} while ($node->tagName !== $token['name']);
|
1664 |
-
}
|
1665 |
-
/*else {
|
1666 |
-
// XERROR: parse error
|
1667 |
-
}*/
|
1668 |
-
break;
|
1669 |
-
|
1670 |
-
/* An end tag whose tag name is one of: "h1", "h2", "h3", "h4",
|
1671 |
-
"h5", "h6" */
|
1672 |
-
case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
|
1673 |
-
$elements = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
1674 |
-
|
1675 |
-
/* If the stack of open elements has in scope an element whose
|
1676 |
-
tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
|
1677 |
-
generate implied end tags. */
|
1678 |
-
if ($this->elementInScope($elements)) {
|
1679 |
-
$this->generateImpliedEndTags();
|
1680 |
-
|
1681 |
-
/* Now, if the current node is not an element with the same
|
1682 |
-
tag name as that of the token, then this is a parse error. */
|
1683 |
-
// XERROR: implement parse error
|
1684 |
-
|
1685 |
-
/* If the stack of open elements has in scope an element
|
1686 |
-
whose tag name is one of "h1", "h2", "h3", "h4", "h5", or
|
1687 |
-
"h6", then pop elements from the stack until an element
|
1688 |
-
with one of those tag names has been popped from the stack. */
|
1689 |
-
do {
|
1690 |
-
$node = array_pop($this->stack);
|
1691 |
-
} while (!in_array($node->tagName, $elements));
|
1692 |
-
}
|
1693 |
-
/*else {
|
1694 |
-
// parse error
|
1695 |
-
}*/
|
1696 |
-
break;
|
1697 |
-
|
1698 |
-
/* An end tag whose tag name is one of: "a", "b", "big", "em",
|
1699 |
-
"font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
|
1700 |
-
case 'a': case 'b': case 'big': case 'code': case 'em': case 'font':
|
1701 |
-
case 'i': case 'nobr': case 's': case 'small': case 'strike':
|
1702 |
-
case 'strong': case 'tt': case 'u':
|
1703 |
-
// XERROR: generally speaking this needs parse error logic
|
1704 |
-
/* 1. Let the formatting element be the last element in
|
1705 |
-
the list of active formatting elements that:
|
1706 |
-
* is between the end of the list and the last scope
|
1707 |
-
marker in the list, if any, or the start of the list
|
1708 |
-
otherwise, and
|
1709 |
-
* has the same tag name as the token.
|
1710 |
-
*/
|
1711 |
-
while (true) {
|
1712 |
-
for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) {
|
1713 |
-
if ($this->a_formatting[$a] === self::MARKER) {
|
1714 |
-
break;
|
1715 |
-
} elseif ($this->a_formatting[$a]->tagName === $token['name']) {
|
1716 |
-
$formatting_element = $this->a_formatting[$a];
|
1717 |
-
$in_stack = in_array($formatting_element, $this->stack, true);
|
1718 |
-
$fe_af_pos = $a;
|
1719 |
-
break;
|
1720 |
-
}
|
1721 |
-
}
|
1722 |
-
|
1723 |
-
/* If there is no such node, or, if that node is
|
1724 |
-
also in the stack of open elements but the element
|
1725 |
-
is not in scope, then this is a parse error. Abort
|
1726 |
-
these steps. The token is ignored. */
|
1727 |
-
if (
|
1728 |
-
!isset($formatting_element) || (
|
1729 |
-
$in_stack &&
|
1730 |
-
!$this->elementInScope($token['name'])
|
1731 |
-
)
|
1732 |
-
) {
|
1733 |
-
$this->ignored = true;
|
1734 |
-
break;
|
1735 |
-
|
1736 |
-
/* Otherwise, if there is such a node, but that node
|
1737 |
-
is not in the stack of open elements, then this is a
|
1738 |
-
parse error; remove the element from the list, and
|
1739 |
-
abort these steps. */
|
1740 |
-
} elseif (isset($formatting_element) && !$in_stack) {
|
1741 |
-
unset($this->a_formatting[$fe_af_pos]);
|
1742 |
-
$this->a_formatting = array_merge($this->a_formatting);
|
1743 |
-
break;
|
1744 |
-
}
|
1745 |
-
|
1746 |
-
/* Otherwise, there is a formatting element and that
|
1747 |
-
* element is in the stack and is in scope. If the
|
1748 |
-
* element is not the current node, this is a parse
|
1749 |
-
* error. In any case, proceed with the algorithm as
|
1750 |
-
* written in the following steps. */
|
1751 |
-
// XERROR: implement me
|
1752 |
-
|
1753 |
-
/* 2. Let the furthest block be the topmost node in the
|
1754 |
-
stack of open elements that is lower in the stack
|
1755 |
-
than the formatting element, and is not an element in
|
1756 |
-
the phrasing or formatting categories. There might
|
1757 |
-
not be one. */
|
1758 |
-
$fe_s_pos = array_search($formatting_element, $this->stack, true);
|
1759 |
-
$length = count($this->stack);
|
1760 |
-
|
1761 |
-
for ($s = $fe_s_pos + 1; $s < $length; $s++) {
|
1762 |
-
$category = $this->getElementCategory($this->stack[$s]);
|
1763 |
-
|
1764 |
-
if ($category !== self::PHRASING && $category !== self::FORMATTING) {
|
1765 |
-
$furthest_block = $this->stack[$s];
|
1766 |
-
break;
|
1767 |
-
}
|
1768 |
-
}
|
1769 |
-
|
1770 |
-
/* 3. If there is no furthest block, then the UA must
|
1771 |
-
skip the subsequent steps and instead just pop all
|
1772 |
-
the nodes from the bottom of the stack of open
|
1773 |
-
elements, from the current node up to the formatting
|
1774 |
-
element, and remove the formatting element from the
|
1775 |
-
list of active formatting elements. */
|
1776 |
-
if (!isset($furthest_block)) {
|
1777 |
-
for ($n = $length - 1; $n >= $fe_s_pos; $n--) {
|
1778 |
-
array_pop($this->stack);
|
1779 |
-
}
|
1780 |
-
|
1781 |
-
unset($this->a_formatting[$fe_af_pos]);
|
1782 |
-
$this->a_formatting = array_merge($this->a_formatting);
|
1783 |
-
break;
|
1784 |
-
}
|
1785 |
-
|
1786 |
-
/* 4. Let the common ancestor be the element
|
1787 |
-
immediately above the formatting element in the stack
|
1788 |
-
of open elements. */
|
1789 |
-
$common_ancestor = $this->stack[$fe_s_pos - 1];
|
1790 |
-
|
1791 |
-
/* 5. Let a bookmark note the position of the
|
1792 |
-
formatting element in the list of active formatting
|
1793 |
-
elements relative to the elements on either side
|
1794 |
-
of it in the list. */
|
1795 |
-
$bookmark = $fe_af_pos;
|
1796 |
-
|
1797 |
-
/* 6. Let node and last node be the furthest block.
|
1798 |
-
Follow these steps: */
|
1799 |
-
$node = $furthest_block;
|
1800 |
-
$last_node = $furthest_block;
|
1801 |
-
|
1802 |
-
while (true) {
|
1803 |
-
for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
|
1804 |
-
/* 6.1 Let node be the element immediately
|
1805 |
-
prior to node in the stack of open elements. */
|
1806 |
-
$node = $this->stack[$n];
|
1807 |
-
|
1808 |
-
/* 6.2 If node is not in the list of active
|
1809 |
-
formatting elements, then remove node from
|
1810 |
-
the stack of open elements and then go back
|
1811 |
-
to step 1. */
|
1812 |
-
if (!in_array($node, $this->a_formatting, true)) {
|
1813 |
-
array_splice($this->stack, $n, 1);
|
1814 |
-
} else {
|
1815 |
-
break;
|
1816 |
-
}
|
1817 |
-
}
|
1818 |
-
|
1819 |
-
/* 6.3 Otherwise, if node is the formatting
|
1820 |
-
element, then go to the next step in the overall
|
1821 |
-
algorithm. */
|
1822 |
-
if ($node === $formatting_element) {
|
1823 |
-
break;
|
1824 |
-
|
1825 |
-
/* 6.4 Otherwise, if last node is the furthest
|
1826 |
-
block, then move the aforementioned bookmark to
|
1827 |
-
be immediately after the node in the list of
|
1828 |
-
active formatting elements. */
|
1829 |
-
} elseif ($last_node === $furthest_block) {
|
1830 |
-
$bookmark = array_search($node, $this->a_formatting, true) + 1;
|
1831 |
-
}
|
1832 |
-
|
1833 |
-
/* 6.5 Create an element for the token for which
|
1834 |
-
* the element node was created, replace the entry
|
1835 |
-
* for node in the list of active formatting
|
1836 |
-
* elements with an entry for the new element,
|
1837 |
-
* replace the entry for node in the stack of open
|
1838 |
-
* elements with an entry for the new element, and
|
1839 |
-
* let node be the new element. */
|
1840 |
-
// we don't know what the token is anymore
|
1841 |
-
// XDOM
|
1842 |
-
$clone = $node->cloneNode();
|
1843 |
-
$a_pos = array_search($node, $this->a_formatting, true);
|
1844 |
-
$s_pos = array_search($node, $this->stack, true);
|
1845 |
-
$this->a_formatting[$a_pos] = $clone;
|
1846 |
-
$this->stack[$s_pos] = $clone;
|
1847 |
-
$node = $clone;
|
1848 |
-
|
1849 |
-
/* 6.6 Insert last node into node, first removing
|
1850 |
-
it from its previous parent node if any. */
|
1851 |
-
// XDOM
|
1852 |
-
if ($last_node->parentNode !== null) {
|
1853 |
-
$last_node->parentNode->removeChild($last_node);
|
1854 |
-
}
|
1855 |
-
|
1856 |
-
// XDOM
|
1857 |
-
$node->appendChild($last_node);
|
1858 |
-
|
1859 |
-
/* 6.7 Let last node be node. */
|
1860 |
-
$last_node = $node;
|
1861 |
-
|
1862 |
-
/* 6.8 Return to step 1 of this inner set of steps. */
|
1863 |
-
}
|
1864 |
-
|
1865 |
-
/* 7. If the common ancestor node is a table, tbody,
|
1866 |
-
* tfoot, thead, or tr element, then, foster parent
|
1867 |
-
* whatever last node ended up being in the previous
|
1868 |
-
* step, first removing it from its previous parent
|
1869 |
-
* node if any. */
|
1870 |
-
// XDOM
|
1871 |
-
if ($last_node->parentNode) { // common step
|
1872 |
-
$last_node->parentNode->removeChild($last_node);
|
1873 |
-
}
|
1874 |
-
if (in_array($common_ancestor->tagName, ['table', 'tbody', 'tfoot', 'thead', 'tr'])) {
|
1875 |
-
$this->fosterParent($last_node);
|
1876 |
-
/* Otherwise, append whatever last node ended up being
|
1877 |
-
* in the previous step to the common ancestor node,
|
1878 |
-
* first removing it from its previous parent node if
|
1879 |
-
* any. */
|
1880 |
-
} else {
|
1881 |
-
// XDOM
|
1882 |
-
$common_ancestor->appendChild($last_node);
|
1883 |
-
}
|
1884 |
-
|
1885 |
-
/* 8. Create an element for the token for which the
|
1886 |
-
* formatting element was created. */
|
1887 |
-
// XDOM
|
1888 |
-
$clone = $formatting_element->cloneNode();
|
1889 |
-
|
1890 |
-
/* 9. Take all of the child nodes of the furthest
|
1891 |
-
block and append them to the element created in the
|
1892 |
-
last step. */
|
1893 |
-
// XDOM
|
1894 |
-
while ($furthest_block->hasChildNodes()) {
|
1895 |
-
$child = $furthest_block->firstChild;
|
1896 |
-
$furthest_block->removeChild($child);
|
1897 |
-
$clone->appendChild($child);
|
1898 |
-
}
|
1899 |
-
|
1900 |
-
/* 10. Append that clone to the furthest block. */
|
1901 |
-
// XDOM
|
1902 |
-
$furthest_block->appendChild($clone);
|
1903 |
-
|
1904 |
-
/* 11. Remove the formatting element from the list
|
1905 |
-
of active formatting elements, and insert the new element
|
1906 |
-
into the list of active formatting elements at the
|
1907 |
-
position of the aforementioned bookmark. */
|
1908 |
-
$fe_af_pos = array_search($formatting_element, $this->a_formatting, true);
|
1909 |
-
array_splice($this->a_formatting, $fe_af_pos, 1);
|
1910 |
-
|
1911 |
-
$af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
|
1912 |
-
$af_part2 = array_slice($this->a_formatting, $bookmark);
|
1913 |
-
$this->a_formatting = array_merge($af_part1, [$clone], $af_part2);
|
1914 |
-
|
1915 |
-
/* 12. Remove the formatting element from the stack
|
1916 |
-
of open elements, and insert the new element into the stack
|
1917 |
-
of open elements immediately below the position of the
|
1918 |
-
furthest block in that stack. */
|
1919 |
-
$fe_s_pos = array_search($formatting_element, $this->stack, true);
|
1920 |
-
array_splice($this->stack, $fe_s_pos, 1);
|
1921 |
-
|
1922 |
-
$fb_s_pos = array_search($furthest_block, $this->stack, true);
|
1923 |
-
$s_part1 = array_slice($this->stack, 0, $fb_s_pos + 1);
|
1924 |
-
$s_part2 = array_slice($this->stack, $fb_s_pos + 1);
|
1925 |
-
$this->stack = array_merge($s_part1, [$clone], $s_part2);
|
1926 |
-
|
1927 |
-
/* 13. Jump back to step 1 in this series of steps. */
|
1928 |
-
unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
|
1929 |
-
}
|
1930 |
-
break;
|
1931 |
-
|
1932 |
-
case 'applet': case 'button': case 'marquee': case 'object':
|
1933 |
-
/* If the stack of open elements has an element in scope whose
|
1934 |
-
tag name matches the tag name of the token, then generate implied
|
1935 |
-
tags. */
|
1936 |
-
if ($this->elementInScope($token['name'])) {
|
1937 |
-
$this->generateImpliedEndTags();
|
1938 |
-
|
1939 |
-
/* Now, if the current node is not an element with the same
|
1940 |
-
tag name as the token, then this is a parse error. */
|
1941 |
-
// XERROR: implement logic
|
1942 |
-
|
1943 |
-
/* Pop elements from the stack of open elements until
|
1944 |
-
* an element with the same tag name as the token has
|
1945 |
-
* been popped from the stack. */
|
1946 |
-
do {
|
1947 |
-
$node = array_pop($this->stack);
|
1948 |
-
} while ($node->tagName !== $token['name']);
|
1949 |
-
|
1950 |
-
/* Clear the list of active formatting elements up to the
|
1951 |
-
* last marker. */
|
1952 |
-
$keys = array_keys($this->a_formatting, self::MARKER, true);
|
1953 |
-
$marker = end($keys);
|
1954 |
-
|
1955 |
-
for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
|
1956 |
-
array_pop($this->a_formatting);
|
1957 |
-
}
|
1958 |
-
}
|
1959 |
-
/*else {
|
1960 |
-
// parse error
|
1961 |
-
}*/
|
1962 |
-
break;
|
1963 |
-
|
1964 |
-
case 'br':
|
1965 |
-
// Parse error
|
1966 |
-
$this->emitToken([
|
1967 |
-
'name' => 'br',
|
1968 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
1969 |
-
]);
|
1970 |
-
break;
|
1971 |
-
|
1972 |
-
/* An end tag token not covered by the previous entries */
|
1973 |
-
default:
|
1974 |
-
for ($n = count($this->stack) - 1; $n >= 0; $n--) {
|
1975 |
-
/* Initialise node to be the current node (the bottommost
|
1976 |
-
node of the stack). */
|
1977 |
-
$node = $this->stack[$n];
|
1978 |
-
|
1979 |
-
/* If node has the same tag name as the end tag token,
|
1980 |
-
then: */
|
1981 |
-
if ($token['name'] === $node->tagName) {
|
1982 |
-
/* Generate implied end tags. */
|
1983 |
-
$this->generateImpliedEndTags();
|
1984 |
-
|
1985 |
-
/* If the tag name of the end tag token does not
|
1986 |
-
match the tag name of the current node, this is a
|
1987 |
-
parse error. */
|
1988 |
-
// XERROR: implement this
|
1989 |
-
|
1990 |
-
/* Pop all the nodes from the current node up to
|
1991 |
-
node, including node, then stop these steps. */
|
1992 |
-
// XSKETCHY
|
1993 |
-
do {
|
1994 |
-
$pop = array_pop($this->stack);
|
1995 |
-
} while ($pop !== $node);
|
1996 |
-
break;
|
1997 |
-
} else {
|
1998 |
-
$category = $this->getElementCategory($node);
|
1999 |
-
|
2000 |
-
if ($category !== self::FORMATTING && $category !== self::PHRASING) {
|
2001 |
-
/* Otherwise, if node is in neither the formatting
|
2002 |
-
category nor the phrasing category, then this is a
|
2003 |
-
parse error. Stop this algorithm. The end tag token
|
2004 |
-
is ignored. */
|
2005 |
-
$this->ignored = true;
|
2006 |
-
break;
|
2007 |
-
// parse error
|
2008 |
-
}
|
2009 |
-
}
|
2010 |
-
/* Set node to the previous entry in the stack of open elements. Loop. */
|
2011 |
-
}
|
2012 |
-
break;
|
2013 |
-
}
|
2014 |
-
break;
|
2015 |
-
}
|
2016 |
-
break;
|
2017 |
-
|
2018 |
-
case self::IN_CDATA_RCDATA:
|
2019 |
-
if (
|
2020 |
-
$token['type'] === HTML5_Tokenizer::CHARACTER ||
|
2021 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER
|
2022 |
-
) {
|
2023 |
-
$this->insertText($token['data']);
|
2024 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
2025 |
-
// parse error
|
2026 |
-
/* If the current node is a script element, mark the script
|
2027 |
-
* element as "already executed". */
|
2028 |
-
// probably not necessary
|
2029 |
-
array_pop($this->stack);
|
2030 |
-
$this->mode = $this->original_mode;
|
2031 |
-
$this->emitToken($token);
|
2032 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'script') {
|
2033 |
-
array_pop($this->stack);
|
2034 |
-
$this->mode = $this->original_mode;
|
2035 |
-
// we're ignoring all of the execution stuff
|
2036 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) {
|
2037 |
-
array_pop($this->stack);
|
2038 |
-
$this->mode = $this->original_mode;
|
2039 |
-
}
|
2040 |
-
break;
|
2041 |
-
|
2042 |
-
case self::IN_TABLE:
|
2043 |
-
$clear = ['html', 'table'];
|
2044 |
-
|
2045 |
-
/* A character token */
|
2046 |
-
if ($token['type'] === HTML5_Tokenizer::CHARACTER ||
|
2047 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2048 |
-
/* Let the pending table character tokens
|
2049 |
-
* be an empty list of tokens. */
|
2050 |
-
$this->pendingTableCharacters = "";
|
2051 |
-
$this->pendingTableCharactersDirty = false;
|
2052 |
-
/* Let the original insertion mode be the current
|
2053 |
-
* insertion mode. */
|
2054 |
-
$this->original_mode = $this->mode;
|
2055 |
-
/* Switch the insertion mode to
|
2056 |
-
* "in table text" and
|
2057 |
-
* reprocess the token. */
|
2058 |
-
$this->mode = self::IN_TABLE_TEXT;
|
2059 |
-
$this->emitToken($token);
|
2060 |
-
|
2061 |
-
/* A comment token */
|
2062 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2063 |
-
/* Append a Comment node to the current node with the data
|
2064 |
-
attribute set to the data given in the comment token. */
|
2065 |
-
$this->insertComment($token['data']);
|
2066 |
-
|
2067 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2068 |
-
// parse error
|
2069 |
-
|
2070 |
-
/* A start tag whose tag name is "caption" */
|
2071 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2072 |
-
$token['name'] === 'caption') {
|
2073 |
-
/* Clear the stack back to a table context. */
|
2074 |
-
$this->clearStackToTableContext($clear);
|
2075 |
-
|
2076 |
-
/* Insert a marker at the end of the list of active
|
2077 |
-
formatting elements. */
|
2078 |
-
$this->a_formatting[] = self::MARKER;
|
2079 |
-
|
2080 |
-
/* Insert an HTML element for the token, then switch the
|
2081 |
-
insertion mode to "in caption". */
|
2082 |
-
$this->insertElement($token);
|
2083 |
-
$this->mode = self::IN_CAPTION;
|
2084 |
-
|
2085 |
-
/* A start tag whose tag name is "colgroup" */
|
2086 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2087 |
-
$token['name'] === 'colgroup') {
|
2088 |
-
/* Clear the stack back to a table context. */
|
2089 |
-
$this->clearStackToTableContext($clear);
|
2090 |
-
|
2091 |
-
/* Insert an HTML element for the token, then switch the
|
2092 |
-
insertion mode to "in column group". */
|
2093 |
-
$this->insertElement($token);
|
2094 |
-
$this->mode = self::IN_COLUMN_GROUP;
|
2095 |
-
|
2096 |
-
/* A start tag whose tag name is "col" */
|
2097 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2098 |
-
$token['name'] === 'col') {
|
2099 |
-
$this->emitToken([
|
2100 |
-
'name' => 'colgroup',
|
2101 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
2102 |
-
'attr' => []
|
2103 |
-
]);
|
2104 |
-
|
2105 |
-
$this->emitToken($token);
|
2106 |
-
|
2107 |
-
/* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
|
2108 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2109 |
-
['tbody', 'tfoot', 'thead'])) {
|
2110 |
-
/* Clear the stack back to a table context. */
|
2111 |
-
$this->clearStackToTableContext($clear);
|
2112 |
-
|
2113 |
-
/* Insert an HTML element for the token, then switch the insertion
|
2114 |
-
mode to "in table body". */
|
2115 |
-
$this->insertElement($token);
|
2116 |
-
$this->mode = self::IN_TABLE_BODY;
|
2117 |
-
|
2118 |
-
/* A start tag whose tag name is one of: "td", "th", "tr" */
|
2119 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2120 |
-
in_array($token['name'], ['td', 'th', 'tr'])) {
|
2121 |
-
/* Act as if a start tag token with the tag name "tbody" had been
|
2122 |
-
seen, then reprocess the current token. */
|
2123 |
-
$this->emitToken([
|
2124 |
-
'name' => 'tbody',
|
2125 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
2126 |
-
'attr' => []
|
2127 |
-
]);
|
2128 |
-
|
2129 |
-
$this->emitToken($token);
|
2130 |
-
|
2131 |
-
/* A start tag whose tag name is "table" */
|
2132 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2133 |
-
$token['name'] === 'table') {
|
2134 |
-
/* Parse error. Act as if an end tag token with the tag name "table"
|
2135 |
-
had been seen, then, if that token wasn't ignored, reprocess the
|
2136 |
-
current token. */
|
2137 |
-
$this->emitToken([
|
2138 |
-
'name' => 'table',
|
2139 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2140 |
-
]);
|
2141 |
-
|
2142 |
-
if (!$this->ignored) {
|
2143 |
-
$this->emitToken($token);
|
2144 |
-
}
|
2145 |
-
|
2146 |
-
/* An end tag whose tag name is "table" */
|
2147 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2148 |
-
$token['name'] === 'table') {
|
2149 |
-
/* If the stack of open elements does not have an element in table
|
2150 |
-
scope with the same tag name as the token, this is a parse error.
|
2151 |
-
Ignore the token. (fragment case) */
|
2152 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2153 |
-
$this->ignored = true;
|
2154 |
-
} else {
|
2155 |
-
do {
|
2156 |
-
$node = array_pop($this->stack);
|
2157 |
-
} while ($node->tagName !== 'table');
|
2158 |
-
|
2159 |
-
/* Reset the insertion mode appropriately. */
|
2160 |
-
$this->resetInsertionMode();
|
2161 |
-
}
|
2162 |
-
|
2163 |
-
/* An end tag whose tag name is one of: "body", "caption", "col",
|
2164 |
-
"colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
|
2165 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2166 |
-
['body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td',
|
2167 |
-
'tfoot', 'th', 'thead', 'tr'])) {
|
2168 |
-
// Parse error. Ignore the token.
|
2169 |
-
|
2170 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2171 |
-
($token['name'] === 'style' || $token['name'] === 'script')) {
|
2172 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
2173 |
-
|
2174 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'input' &&
|
2175 |
-
// assignment is intentional
|
2176 |
-
/* If the token does not have an attribute with the name "type", or
|
2177 |
-
* if it does, but that attribute's value is not an ASCII
|
2178 |
-
* case-insensitive match for the string "hidden", then: act as
|
2179 |
-
* described in the "anything else" entry below. */
|
2180 |
-
($type = $this->getAttr($token, 'type')) && strtolower($type) === 'hidden') {
|
2181 |
-
// I.e., if its an input with the type attribute == 'hidden'
|
2182 |
-
/* Otherwise */
|
2183 |
-
// parse error
|
2184 |
-
$this->insertElement($token);
|
2185 |
-
array_pop($this->stack);
|
2186 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
2187 |
-
/* If the current node is not the root html element, then this is a parse error. */
|
2188 |
-
if (end($this->stack)->tagName !== 'html') {
|
2189 |
-
// Note: It can only be the current node in the fragment case.
|
2190 |
-
// parse error
|
2191 |
-
}
|
2192 |
-
/* Stop parsing. */
|
2193 |
-
/* Anything else */
|
2194 |
-
} else {
|
2195 |
-
/* Parse error. Process the token as if the insertion mode was "in
|
2196 |
-
body", with the following exception: */
|
2197 |
-
|
2198 |
-
$old = $this->foster_parent;
|
2199 |
-
$this->foster_parent = true;
|
2200 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2201 |
-
$this->foster_parent = $old;
|
2202 |
-
}
|
2203 |
-
break;
|
2204 |
-
|
2205 |
-
case self::IN_TABLE_TEXT:
|
2206 |
-
/* A character token */
|
2207 |
-
if ($token['type'] === HTML5_Tokenizer::CHARACTER) {
|
2208 |
-
/* Append the character token to the pending table
|
2209 |
-
* character tokens list. */
|
2210 |
-
$this->pendingTableCharacters .= $token['data'];
|
2211 |
-
$this->pendingTableCharactersDirty = true;
|
2212 |
-
} elseif ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2213 |
-
$this->pendingTableCharacters .= $token['data'];
|
2214 |
-
/* Anything else */
|
2215 |
-
} else {
|
2216 |
-
if ($this->pendingTableCharacters !== '' && is_string($this->pendingTableCharacters)) {
|
2217 |
-
/* If any of the tokens in the pending table character tokens list
|
2218 |
-
* are character tokens that are not one of U+0009 CHARACTER
|
2219 |
-
* TABULATION, U+000A LINE FEED (LF), U+000C FORM FEED (FF), or
|
2220 |
-
* U+0020 SPACE, then reprocess those character tokens using the
|
2221 |
-
* rules given in the "anything else" entry in the in table"
|
2222 |
-
* insertion mode.*/
|
2223 |
-
if ($this->pendingTableCharactersDirty) {
|
2224 |
-
/* Parse error. Process the token using the rules for the
|
2225 |
-
* "in body" insertion mode, except that if the current
|
2226 |
-
* node is a table, tbody, tfoot, thead, or tr element,
|
2227 |
-
* then, whenever a node would be inserted into the current
|
2228 |
-
* node, it must instead be foster parented. */
|
2229 |
-
// XERROR
|
2230 |
-
$old = $this->foster_parent;
|
2231 |
-
$this->foster_parent = true;
|
2232 |
-
$text_token = [
|
2233 |
-
'type' => HTML5_Tokenizer::CHARACTER,
|
2234 |
-
'data' => $this->pendingTableCharacters,
|
2235 |
-
];
|
2236 |
-
$this->processWithRulesFor($text_token, self::IN_BODY);
|
2237 |
-
$this->foster_parent = $old;
|
2238 |
-
|
2239 |
-
/* Otherwise, insert the characters given by the pending table
|
2240 |
-
* character tokens list into the current node. */
|
2241 |
-
} else {
|
2242 |
-
$this->insertText($this->pendingTableCharacters);
|
2243 |
-
}
|
2244 |
-
$this->pendingTableCharacters = null;
|
2245 |
-
$this->pendingTableCharactersNull = null;
|
2246 |
-
}
|
2247 |
-
|
2248 |
-
/* Switch the insertion mode to the original insertion mode and
|
2249 |
-
* reprocess the token.
|
2250 |
-
*/
|
2251 |
-
$this->mode = $this->original_mode;
|
2252 |
-
$this->emitToken($token);
|
2253 |
-
}
|
2254 |
-
break;
|
2255 |
-
|
2256 |
-
case self::IN_CAPTION:
|
2257 |
-
/* An end tag whose tag name is "caption" */
|
2258 |
-
if ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'caption') {
|
2259 |
-
/* If the stack of open elements does not have an element in table
|
2260 |
-
scope with the same tag name as the token, this is a parse error.
|
2261 |
-
Ignore the token. (fragment case) */
|
2262 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2263 |
-
$this->ignored = true;
|
2264 |
-
// Ignore
|
2265 |
-
|
2266 |
-
/* Otherwise: */
|
2267 |
-
} else {
|
2268 |
-
/* Generate implied end tags. */
|
2269 |
-
$this->generateImpliedEndTags();
|
2270 |
-
|
2271 |
-
/* Now, if the current node is not a caption element, then this
|
2272 |
-
is a parse error. */
|
2273 |
-
// XERROR: implement
|
2274 |
-
|
2275 |
-
/* Pop elements from this stack until a caption element has
|
2276 |
-
been popped from the stack. */
|
2277 |
-
do {
|
2278 |
-
$node = array_pop($this->stack);
|
2279 |
-
} while ($node->tagName !== 'caption');
|
2280 |
-
|
2281 |
-
/* Clear the list of active formatting elements up to the last
|
2282 |
-
marker. */
|
2283 |
-
$this->clearTheActiveFormattingElementsUpToTheLastMarker();
|
2284 |
-
|
2285 |
-
/* Switch the insertion mode to "in table". */
|
2286 |
-
$this->mode = self::IN_TABLE;
|
2287 |
-
}
|
2288 |
-
|
2289 |
-
/* A start tag whose tag name is one of: "caption", "col", "colgroup",
|
2290 |
-
"tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag
|
2291 |
-
name is "table" */
|
2292 |
-
} elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2293 |
-
['caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
|
2294 |
-
'thead', 'tr'])) || ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2295 |
-
$token['name'] === 'table')) {
|
2296 |
-
/* Parse error. Act as if an end tag with the tag name "caption"
|
2297 |
-
had been seen, then, if that token wasn't ignored, reprocess the
|
2298 |
-
current token. */
|
2299 |
-
$this->emitToken([
|
2300 |
-
'name' => 'caption',
|
2301 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2302 |
-
]);
|
2303 |
-
|
2304 |
-
if (!$this->ignored) {
|
2305 |
-
$this->emitToken($token);
|
2306 |
-
}
|
2307 |
-
|
2308 |
-
/* An end tag whose tag name is one of: "body", "col", "colgroup",
|
2309 |
-
"html", "tbody", "td", "tfoot", "th", "thead", "tr" */
|
2310 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2311 |
-
['body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th',
|
2312 |
-
'thead', 'tr'])) {
|
2313 |
-
// Parse error. Ignore the token.
|
2314 |
-
$this->ignored = true;
|
2315 |
-
} else {
|
2316 |
-
/* Process the token as if the insertion mode was "in body". */
|
2317 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2318 |
-
}
|
2319 |
-
break;
|
2320 |
-
|
2321 |
-
case self::IN_COLUMN_GROUP:
|
2322 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
2323 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
2324 |
-
or U+0020 SPACE */
|
2325 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2326 |
-
/* Append the character to the current node. */
|
2327 |
-
$this->insertText($token['data']);
|
2328 |
-
|
2329 |
-
/* A comment token */
|
2330 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2331 |
-
/* Append a Comment node to the current node with the data
|
2332 |
-
attribute set to the data given in the comment token. */
|
2333 |
-
$this->insertComment($token['data']);
|
2334 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2335 |
-
// parse error
|
2336 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
2337 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2338 |
-
|
2339 |
-
/* A start tag whose tag name is "col" */
|
2340 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'col') {
|
2341 |
-
/* Insert a col element for the token. Immediately pop the current
|
2342 |
-
node off the stack of open elements. */
|
2343 |
-
$this->insertElement($token);
|
2344 |
-
array_pop($this->stack);
|
2345 |
-
// XERROR: Acknowledge the token's self-closing flag, if it is set.
|
2346 |
-
|
2347 |
-
/* An end tag whose tag name is "colgroup" */
|
2348 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2349 |
-
$token['name'] === 'colgroup') {
|
2350 |
-
/* If the current node is the root html element, then this is a
|
2351 |
-
parse error, ignore the token. (fragment case) */
|
2352 |
-
if (end($this->stack)->tagName === 'html') {
|
2353 |
-
$this->ignored = true;
|
2354 |
-
|
2355 |
-
/* Otherwise, pop the current node (which will be a colgroup
|
2356 |
-
element) from the stack of open elements. Switch the insertion
|
2357 |
-
mode to "in table". */
|
2358 |
-
} else {
|
2359 |
-
array_pop($this->stack);
|
2360 |
-
$this->mode = self::IN_TABLE;
|
2361 |
-
}
|
2362 |
-
|
2363 |
-
/* An end tag whose tag name is "col" */
|
2364 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'col') {
|
2365 |
-
/* Parse error. Ignore the token. */
|
2366 |
-
$this->ignored = true;
|
2367 |
-
|
2368 |
-
/* An end-of-file token */
|
2369 |
-
/* If the current node is the root html element */
|
2370 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF && end($this->stack)->tagName === 'html') {
|
2371 |
-
/* Stop parsing */
|
2372 |
-
|
2373 |
-
/* Anything else */
|
2374 |
-
} else {
|
2375 |
-
/* Act as if an end tag with the tag name "colgroup" had been seen,
|
2376 |
-
and then, if that token wasn't ignored, reprocess the current token. */
|
2377 |
-
$this->emitToken([
|
2378 |
-
'name' => 'colgroup',
|
2379 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2380 |
-
]);
|
2381 |
-
|
2382 |
-
if (!$this->ignored) {
|
2383 |
-
$this->emitToken($token);
|
2384 |
-
}
|
2385 |
-
}
|
2386 |
-
break;
|
2387 |
-
|
2388 |
-
case self::IN_TABLE_BODY:
|
2389 |
-
$clear = ['tbody', 'tfoot', 'thead', 'html'];
|
2390 |
-
|
2391 |
-
/* A start tag whose tag name is "tr" */
|
2392 |
-
if ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'tr') {
|
2393 |
-
/* Clear the stack back to a table body context. */
|
2394 |
-
$this->clearStackToTableContext($clear);
|
2395 |
-
|
2396 |
-
/* Insert a tr element for the token, then switch the insertion
|
2397 |
-
mode to "in row". */
|
2398 |
-
$this->insertElement($token);
|
2399 |
-
$this->mode = self::IN_ROW;
|
2400 |
-
|
2401 |
-
/* A start tag whose tag name is one of: "th", "td" */
|
2402 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2403 |
-
($token['name'] === 'th' || $token['name'] === 'td')) {
|
2404 |
-
/* Parse error. Act as if a start tag with the tag name "tr" had
|
2405 |
-
been seen, then reprocess the current token. */
|
2406 |
-
$this->emitToken([
|
2407 |
-
'name' => 'tr',
|
2408 |
-
'type' => HTML5_Tokenizer::STARTTAG,
|
2409 |
-
'attr' => []
|
2410 |
-
]);
|
2411 |
-
|
2412 |
-
$this->emitToken($token);
|
2413 |
-
|
2414 |
-
/* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
|
2415 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2416 |
-
in_array($token['name'], ['tbody', 'tfoot', 'thead'])) {
|
2417 |
-
/* If the stack of open elements does not have an element in table
|
2418 |
-
scope with the same tag name as the token, this is a parse error.
|
2419 |
-
Ignore the token. */
|
2420 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2421 |
-
// Parse error
|
2422 |
-
$this->ignored = true;
|
2423 |
-
|
2424 |
-
/* Otherwise: */
|
2425 |
-
} else {
|
2426 |
-
/* Clear the stack back to a table body context. */
|
2427 |
-
$this->clearStackToTableContext($clear);
|
2428 |
-
|
2429 |
-
/* Pop the current node from the stack of open elements. Switch
|
2430 |
-
the insertion mode to "in table". */
|
2431 |
-
array_pop($this->stack);
|
2432 |
-
$this->mode = self::IN_TABLE;
|
2433 |
-
}
|
2434 |
-
|
2435 |
-
/* A start tag whose tag name is one of: "caption", "col", "colgroup",
|
2436 |
-
"tbody", "tfoot", "thead", or an end tag whose tag name is "table" */
|
2437 |
-
} elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2438 |
-
['caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead'])) ||
|
2439 |
-
($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) {
|
2440 |
-
/* If the stack of open elements does not have a tbody, thead, or
|
2441 |
-
tfoot element in table scope, this is a parse error. Ignore the
|
2442 |
-
token. (fragment case) */
|
2443 |
-
if (!$this->elementInScope(['tbody', 'thead', 'tfoot'], self::SCOPE_TABLE)) {
|
2444 |
-
// parse error
|
2445 |
-
$this->ignored = true;
|
2446 |
-
|
2447 |
-
/* Otherwise: */
|
2448 |
-
} else {
|
2449 |
-
/* Clear the stack back to a table body context. */
|
2450 |
-
$this->clearStackToTableContext($clear);
|
2451 |
-
|
2452 |
-
/* Act as if an end tag with the same tag name as the current
|
2453 |
-
node ("tbody", "tfoot", or "thead") had been seen, then
|
2454 |
-
reprocess the current token. */
|
2455 |
-
$this->emitToken([
|
2456 |
-
'name' => end($this->stack)->tagName,
|
2457 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2458 |
-
]);
|
2459 |
-
|
2460 |
-
$this->emitToken($token);
|
2461 |
-
}
|
2462 |
-
|
2463 |
-
/* An end tag whose tag name is one of: "body", "caption", "col",
|
2464 |
-
"colgroup", "html", "td", "th", "tr" */
|
2465 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2466 |
-
['body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'])) {
|
2467 |
-
/* Parse error. Ignore the token. */
|
2468 |
-
$this->ignored = true;
|
2469 |
-
|
2470 |
-
/* Anything else */
|
2471 |
-
} else {
|
2472 |
-
/* Process the token as if the insertion mode was "in table". */
|
2473 |
-
$this->processWithRulesFor($token, self::IN_TABLE);
|
2474 |
-
}
|
2475 |
-
break;
|
2476 |
-
|
2477 |
-
case self::IN_ROW:
|
2478 |
-
$clear = ['tr', 'html'];
|
2479 |
-
|
2480 |
-
/* A start tag whose tag name is one of: "th", "td" */
|
2481 |
-
if ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2482 |
-
($token['name'] === 'th' || $token['name'] === 'td')) {
|
2483 |
-
/* Clear the stack back to a table row context. */
|
2484 |
-
$this->clearStackToTableContext($clear);
|
2485 |
-
|
2486 |
-
/* Insert an HTML element for the token, then switch the insertion
|
2487 |
-
mode to "in cell". */
|
2488 |
-
$this->insertElement($token);
|
2489 |
-
$this->mode = self::IN_CELL;
|
2490 |
-
|
2491 |
-
/* Insert a marker at the end of the list of active formatting
|
2492 |
-
elements. */
|
2493 |
-
$this->a_formatting[] = self::MARKER;
|
2494 |
-
|
2495 |
-
/* An end tag whose tag name is "tr" */
|
2496 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'tr') {
|
2497 |
-
/* If the stack of open elements does not have an element in table
|
2498 |
-
scope with the same tag name as the token, this is a parse error.
|
2499 |
-
Ignore the token. (fragment case) */
|
2500 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2501 |
-
// Ignore.
|
2502 |
-
$this->ignored = true;
|
2503 |
-
} else {
|
2504 |
-
/* Clear the stack back to a table row context. */
|
2505 |
-
$this->clearStackToTableContext($clear);
|
2506 |
-
|
2507 |
-
/* Pop the current node (which will be a tr element) from the
|
2508 |
-
stack of open elements. Switch the insertion mode to "in table
|
2509 |
-
body". */
|
2510 |
-
array_pop($this->stack);
|
2511 |
-
$this->mode = self::IN_TABLE_BODY;
|
2512 |
-
}
|
2513 |
-
|
2514 |
-
/* A start tag whose tag name is one of: "caption", "col", "colgroup",
|
2515 |
-
"tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */
|
2516 |
-
} elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2517 |
-
['caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'])) ||
|
2518 |
-
($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) {
|
2519 |
-
/* Act as if an end tag with the tag name "tr" had been seen, then,
|
2520 |
-
if that token wasn't ignored, reprocess the current token. */
|
2521 |
-
$this->emitToken([
|
2522 |
-
'name' => 'tr',
|
2523 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2524 |
-
]);
|
2525 |
-
if (!$this->ignored) {
|
2526 |
-
$this->emitToken($token);
|
2527 |
-
}
|
2528 |
-
|
2529 |
-
/* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
|
2530 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2531 |
-
in_array($token['name'], ['tbody', 'tfoot', 'thead'])) {
|
2532 |
-
/* If the stack of open elements does not have an element in table
|
2533 |
-
scope with the same tag name as the token, this is a parse error.
|
2534 |
-
Ignore the token. */
|
2535 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2536 |
-
$this->ignored = true;
|
2537 |
-
|
2538 |
-
/* Otherwise: */
|
2539 |
-
} else {
|
2540 |
-
/* Otherwise, act as if an end tag with the tag name "tr" had
|
2541 |
-
been seen, then reprocess the current token. */
|
2542 |
-
$this->emitToken([
|
2543 |
-
'name' => 'tr',
|
2544 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2545 |
-
]);
|
2546 |
-
|
2547 |
-
$this->emitToken($token);
|
2548 |
-
}
|
2549 |
-
|
2550 |
-
/* An end tag whose tag name is one of: "body", "caption", "col",
|
2551 |
-
"colgroup", "html", "td", "th" */
|
2552 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2553 |
-
['body', 'caption', 'col', 'colgroup', 'html', 'td', 'th'])) {
|
2554 |
-
/* Parse error. Ignore the token. */
|
2555 |
-
$this->ignored = true;
|
2556 |
-
|
2557 |
-
/* Anything else */
|
2558 |
-
} else {
|
2559 |
-
/* Process the token as if the insertion mode was "in table". */
|
2560 |
-
$this->processWithRulesFor($token, self::IN_TABLE);
|
2561 |
-
}
|
2562 |
-
break;
|
2563 |
-
|
2564 |
-
case self::IN_CELL:
|
2565 |
-
/* An end tag whose tag name is one of: "td", "th" */
|
2566 |
-
if ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2567 |
-
($token['name'] === 'td' || $token['name'] === 'th')) {
|
2568 |
-
/* If the stack of open elements does not have an element in table
|
2569 |
-
scope with the same tag name as that of the token, then this is a
|
2570 |
-
parse error and the token must be ignored. */
|
2571 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2572 |
-
$this->ignored = true;
|
2573 |
-
|
2574 |
-
/* Otherwise: */
|
2575 |
-
} else {
|
2576 |
-
/* Generate implied end tags, except for elements with the same
|
2577 |
-
tag name as the token. */
|
2578 |
-
$this->generateImpliedEndTags([$token['name']]);
|
2579 |
-
|
2580 |
-
/* Now, if the current node is not an element with the same tag
|
2581 |
-
name as the token, then this is a parse error. */
|
2582 |
-
// XERROR: Implement parse error code
|
2583 |
-
|
2584 |
-
/* Pop elements from this stack until an element with the same
|
2585 |
-
tag name as the token has been popped from the stack. */
|
2586 |
-
do {
|
2587 |
-
$node = array_pop($this->stack);
|
2588 |
-
} while ($node->tagName !== $token['name']);
|
2589 |
-
|
2590 |
-
/* Clear the list of active formatting elements up to the last
|
2591 |
-
marker. */
|
2592 |
-
$this->clearTheActiveFormattingElementsUpToTheLastMarker();
|
2593 |
-
|
2594 |
-
/* Switch the insertion mode to "in row". (The current node
|
2595 |
-
will be a tr element at this point.) */
|
2596 |
-
$this->mode = self::IN_ROW;
|
2597 |
-
}
|
2598 |
-
|
2599 |
-
/* A start tag whose tag name is one of: "caption", "col", "colgroup",
|
2600 |
-
"tbody", "td", "tfoot", "th", "thead", "tr" */
|
2601 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
|
2602 |
-
['caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
|
2603 |
-
'thead', 'tr'])) {
|
2604 |
-
/* If the stack of open elements does not have a td or th element
|
2605 |
-
in table scope, then this is a parse error; ignore the token.
|
2606 |
-
(fragment case) */
|
2607 |
-
if (!$this->elementInScope(['td', 'th'], self::SCOPE_TABLE)) {
|
2608 |
-
// parse error
|
2609 |
-
$this->ignored = true;
|
2610 |
-
|
2611 |
-
/* Otherwise, close the cell (see below) and reprocess the current
|
2612 |
-
token. */
|
2613 |
-
} else {
|
2614 |
-
$this->closeCell();
|
2615 |
-
$this->emitToken($token);
|
2616 |
-
}
|
2617 |
-
|
2618 |
-
/* An end tag whose tag name is one of: "body", "caption", "col",
|
2619 |
-
"colgroup", "html" */
|
2620 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2621 |
-
['body', 'caption', 'col', 'colgroup', 'html'])) {
|
2622 |
-
/* Parse error. Ignore the token. */
|
2623 |
-
$this->ignored = true;
|
2624 |
-
|
2625 |
-
/* An end tag whose tag name is one of: "table", "tbody", "tfoot",
|
2626 |
-
"thead", "tr" */
|
2627 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
|
2628 |
-
['table', 'tbody', 'tfoot', 'thead', 'tr'])) {
|
2629 |
-
/* If the stack of open elements does not have a td or th element
|
2630 |
-
in table scope, then this is a parse error; ignore the token.
|
2631 |
-
(innerHTML case) */
|
2632 |
-
if (!$this->elementInScope(['td', 'th'], self::SCOPE_TABLE)) {
|
2633 |
-
// Parse error
|
2634 |
-
$this->ignored = true;
|
2635 |
-
|
2636 |
-
/* Otherwise, close the cell (see below) and reprocess the current
|
2637 |
-
token. */
|
2638 |
-
} else {
|
2639 |
-
$this->closeCell();
|
2640 |
-
$this->emitToken($token);
|
2641 |
-
}
|
2642 |
-
|
2643 |
-
/* Anything else */
|
2644 |
-
} else {
|
2645 |
-
/* Process the token as if the insertion mode was "in body". */
|
2646 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2647 |
-
}
|
2648 |
-
break;
|
2649 |
-
|
2650 |
-
case self::IN_SELECT:
|
2651 |
-
/* Handle the token as follows: */
|
2652 |
-
|
2653 |
-
/* A character token */
|
2654 |
-
if (
|
2655 |
-
$token['type'] === HTML5_Tokenizer::CHARACTER ||
|
2656 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER
|
2657 |
-
) {
|
2658 |
-
/* Append the token's character to the current node. */
|
2659 |
-
$this->insertText($token['data']);
|
2660 |
-
|
2661 |
-
/* A comment token */
|
2662 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2663 |
-
/* Append a Comment node to the current node with the data
|
2664 |
-
attribute set to the data given in the comment token. */
|
2665 |
-
$this->insertComment($token['data']);
|
2666 |
-
|
2667 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2668 |
-
// parse error
|
2669 |
-
|
2670 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
2671 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2672 |
-
|
2673 |
-
/* A start tag token whose tag name is "option" */
|
2674 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2675 |
-
$token['name'] === 'option') {
|
2676 |
-
/* If the current node is an option element, act as if an end tag
|
2677 |
-
with the tag name "option" had been seen. */
|
2678 |
-
if (end($this->stack)->tagName === 'option') {
|
2679 |
-
$this->emitToken([
|
2680 |
-
'name' => 'option',
|
2681 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2682 |
-
]);
|
2683 |
-
}
|
2684 |
-
|
2685 |
-
/* Insert an HTML element for the token. */
|
2686 |
-
$this->insertElement($token);
|
2687 |
-
|
2688 |
-
/* A start tag token whose tag name is "optgroup" */
|
2689 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2690 |
-
$token['name'] === 'optgroup') {
|
2691 |
-
/* If the current node is an option element, act as if an end tag
|
2692 |
-
with the tag name "option" had been seen. */
|
2693 |
-
if (end($this->stack)->tagName === 'option') {
|
2694 |
-
$this->emitToken([
|
2695 |
-
'name' => 'option',
|
2696 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2697 |
-
]);
|
2698 |
-
}
|
2699 |
-
|
2700 |
-
/* If the current node is an optgroup element, act as if an end tag
|
2701 |
-
with the tag name "optgroup" had been seen. */
|
2702 |
-
if (end($this->stack)->tagName === 'optgroup') {
|
2703 |
-
$this->emitToken([
|
2704 |
-
'name' => 'optgroup',
|
2705 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2706 |
-
]);
|
2707 |
-
}
|
2708 |
-
|
2709 |
-
/* Insert an HTML element for the token. */
|
2710 |
-
$this->insertElement($token);
|
2711 |
-
|
2712 |
-
/* An end tag token whose tag name is "optgroup" */
|
2713 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2714 |
-
$token['name'] === 'optgroup') {
|
2715 |
-
/* First, if the current node is an option element, and the node
|
2716 |
-
immediately before it in the stack of open elements is an optgroup
|
2717 |
-
element, then act as if an end tag with the tag name "option" had
|
2718 |
-
been seen. */
|
2719 |
-
$elements_in_stack = count($this->stack);
|
2720 |
-
|
2721 |
-
if ($this->stack[$elements_in_stack - 1]->tagName === 'option' &&
|
2722 |
-
$this->stack[$elements_in_stack - 2]->tagName === 'optgroup') {
|
2723 |
-
$this->emitToken([
|
2724 |
-
'name' => 'option',
|
2725 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2726 |
-
]);
|
2727 |
-
}
|
2728 |
-
|
2729 |
-
/* If the current node is an optgroup element, then pop that node
|
2730 |
-
from the stack of open elements. Otherwise, this is a parse error,
|
2731 |
-
ignore the token. */
|
2732 |
-
if (end($this->stack)->tagName === 'optgroup') {
|
2733 |
-
array_pop($this->stack);
|
2734 |
-
} else {
|
2735 |
-
// parse error
|
2736 |
-
$this->ignored = true;
|
2737 |
-
}
|
2738 |
-
|
2739 |
-
/* An end tag token whose tag name is "option" */
|
2740 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2741 |
-
$token['name'] === 'option') {
|
2742 |
-
/* If the current node is an option element, then pop that node
|
2743 |
-
from the stack of open elements. Otherwise, this is a parse error,
|
2744 |
-
ignore the token. */
|
2745 |
-
if (end($this->stack)->tagName === 'option') {
|
2746 |
-
array_pop($this->stack);
|
2747 |
-
} else {
|
2748 |
-
// parse error
|
2749 |
-
$this->ignored = true;
|
2750 |
-
}
|
2751 |
-
|
2752 |
-
/* An end tag whose tag name is "select" */
|
2753 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2754 |
-
$token['name'] === 'select') {
|
2755 |
-
/* If the stack of open elements does not have an element in table
|
2756 |
-
scope with the same tag name as the token, this is a parse error.
|
2757 |
-
Ignore the token. (fragment case) */
|
2758 |
-
if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2759 |
-
$this->ignored = true;
|
2760 |
-
// parse error
|
2761 |
-
|
2762 |
-
/* Otherwise: */
|
2763 |
-
} else {
|
2764 |
-
/* Pop elements from the stack of open elements until a select
|
2765 |
-
element has been popped from the stack. */
|
2766 |
-
do {
|
2767 |
-
$node = array_pop($this->stack);
|
2768 |
-
} while ($node->tagName !== 'select');
|
2769 |
-
|
2770 |
-
/* Reset the insertion mode appropriately. */
|
2771 |
-
$this->resetInsertionMode();
|
2772 |
-
}
|
2773 |
-
|
2774 |
-
/* A start tag whose tag name is "select" */
|
2775 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'select') {
|
2776 |
-
/* Parse error. Act as if the token had been an end tag with the
|
2777 |
-
tag name "select" instead. */
|
2778 |
-
$this->emitToken([
|
2779 |
-
'name' => 'select',
|
2780 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2781 |
-
]);
|
2782 |
-
|
2783 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2784 |
-
($token['name'] === 'input' || $token['name'] === 'keygen' || $token['name'] === 'textarea')) {
|
2785 |
-
// parse error
|
2786 |
-
$this->emitToken([
|
2787 |
-
'name' => 'select',
|
2788 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2789 |
-
]);
|
2790 |
-
$this->emitToken($token);
|
2791 |
-
|
2792 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') {
|
2793 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
2794 |
-
|
2795 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
2796 |
-
// XERROR: If the current node is not the root html element, then this is a parse error.
|
2797 |
-
/* Stop parsing */
|
2798 |
-
|
2799 |
-
/* Anything else */
|
2800 |
-
} else {
|
2801 |
-
/* Parse error. Ignore the token. */
|
2802 |
-
$this->ignored = true;
|
2803 |
-
}
|
2804 |
-
break;
|
2805 |
-
|
2806 |
-
case self::IN_SELECT_IN_TABLE:
|
2807 |
-
|
2808 |
-
if ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2809 |
-
in_array($token['name'], ['caption', 'table', 'tbody',
|
2810 |
-
'tfoot', 'thead', 'tr', 'td', 'th'])) {
|
2811 |
-
// parse error
|
2812 |
-
$this->emitToken([
|
2813 |
-
'name' => 'select',
|
2814 |
-
'type' => HTML5_Tokenizer::ENDTAG,
|
2815 |
-
]);
|
2816 |
-
$this->emitToken($token);
|
2817 |
-
|
2818 |
-
/* An end tag whose tag name is one of: "caption", "table", "tbody",
|
2819 |
-
"tfoot", "thead", "tr", "td", "th" */
|
2820 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2821 |
-
in_array($token['name'], ['caption', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'td', 'th'])) {
|
2822 |
-
/* Parse error. */
|
2823 |
-
// parse error
|
2824 |
-
|
2825 |
-
/* If the stack of open elements has an element in table scope with
|
2826 |
-
the same tag name as that of the token, then act as if an end tag
|
2827 |
-
with the tag name "select" had been seen, and reprocess the token.
|
2828 |
-
Otherwise, ignore the token. */
|
2829 |
-
if ($this->elementInScope($token['name'], self::SCOPE_TABLE)) {
|
2830 |
-
$this->emitToken([
|
2831 |
-
'name' => 'select',
|
2832 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
2833 |
-
]);
|
2834 |
-
|
2835 |
-
$this->emitToken($token);
|
2836 |
-
} else {
|
2837 |
-
$this->ignored = true;
|
2838 |
-
}
|
2839 |
-
} else {
|
2840 |
-
$this->processWithRulesFor($token, self::IN_SELECT);
|
2841 |
-
}
|
2842 |
-
break;
|
2843 |
-
|
2844 |
-
case self::IN_FOREIGN_CONTENT:
|
2845 |
-
if ($token['type'] === HTML5_Tokenizer::CHARACTER ||
|
2846 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2847 |
-
$this->insertText($token['data']);
|
2848 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2849 |
-
$this->insertComment($token['data']);
|
2850 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
2851 |
-
// XERROR: parse error
|
2852 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
2853 |
-
$token['name'] === 'script' && end($this->stack)->tagName === 'script' &&
|
2854 |
-
// XDOM
|
2855 |
-
end($this->stack)->namespaceURI === self::NS_SVG) {
|
2856 |
-
array_pop($this->stack);
|
2857 |
-
// a bunch of script running mumbo jumbo
|
2858 |
-
} elseif (
|
2859 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2860 |
-
((
|
2861 |
-
$token['name'] !== 'mglyph' &&
|
2862 |
-
$token['name'] !== 'malignmark' &&
|
2863 |
-
// XDOM
|
2864 |
-
end($this->stack)->namespaceURI === self::NS_MATHML &&
|
2865 |
-
in_array(end($this->stack)->tagName, ['mi', 'mo', 'mn', 'ms', 'mtext'])
|
2866 |
-
) ||
|
2867 |
-
(
|
2868 |
-
$token['name'] === 'svg' &&
|
2869 |
-
// XDOM
|
2870 |
-
end($this->stack)->namespaceURI === self::NS_MATHML &&
|
2871 |
-
end($this->stack)->tagName === 'annotation-xml'
|
2872 |
-
) ||
|
2873 |
-
(
|
2874 |
-
// XDOM
|
2875 |
-
end($this->stack)->namespaceURI === self::NS_SVG &&
|
2876 |
-
in_array(end($this->stack)->tagName, ['foreignObject', 'desc', 'title'])
|
2877 |
-
) ||
|
2878 |
-
(
|
2879 |
-
// XSKETCHY && XDOM
|
2880 |
-
end($this->stack)->namespaceURI === self::NS_HTML
|
2881 |
-
))
|
2882 |
-
) || $token['type'] === HTML5_Tokenizer::ENDTAG
|
2883 |
-
) {
|
2884 |
-
$this->processWithRulesFor($token, $this->secondary_mode);
|
2885 |
-
/* If, after doing so, the insertion mode is still "in foreign
|
2886 |
-
* content", but there is no element in scope that has a namespace
|
2887 |
-
* other than the HTML namespace, switch the insertion mode to the
|
2888 |
-
* secondary insertion mode. */
|
2889 |
-
if ($this->mode === self::IN_FOREIGN_CONTENT) {
|
2890 |
-
$found = false;
|
2891 |
-
// this basically duplicates elementInScope()
|
2892 |
-
for ($i = count($this->stack) - 1; $i >= 0; $i--) {
|
2893 |
-
// XDOM
|
2894 |
-
$node = $this->stack[$i];
|
2895 |
-
if ($node->namespaceURI !== self::NS_HTML) {
|
2896 |
-
$found = true;
|
2897 |
-
break;
|
2898 |
-
} elseif (in_array($node->tagName, ['table', 'html',
|
2899 |
-
'applet', 'caption', 'td', 'th', 'button', 'marquee',
|
2900 |
-
'object']) || ($node->tagName === 'foreignObject' &&
|
2901 |
-
$node->namespaceURI === self::NS_SVG)) {
|
2902 |
-
break;
|
2903 |
-
}
|
2904 |
-
}
|
2905 |
-
if (!$found) {
|
2906 |
-
$this->mode = $this->secondary_mode;
|
2907 |
-
}
|
2908 |
-
}
|
2909 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF || (
|
2910 |
-
$token['type'] === HTML5_Tokenizer::STARTTAG &&
|
2911 |
-
(in_array($token['name'], ['b', "big", "blockquote", "body", "br",
|
2912 |
-
"center", "code", "dc", "dd", "div", "dl", "ds", "dt", "em", "embed", "h1", "h2",
|
2913 |
-
"h3", "h4", "h5", "h6", "head", "hr", "i", "img", "li", "listing",
|
2914 |
-
"menu", "meta", "nobr", "ol", "p", "pre", "ruby", "s", "small",
|
2915 |
-
"span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul",
|
2916 |
-
"var"]) || ($token['name'] === 'font' && ($this->getAttr($token, 'color') ||
|
2917 |
-
$this->getAttr($token, 'face') || $this->getAttr($token, 'size')))))) {
|
2918 |
-
// XERROR: parse error
|
2919 |
-
do {
|
2920 |
-
$node = array_pop($this->stack);
|
2921 |
-
// XDOM
|
2922 |
-
} while ($node->namespaceURI !== self::NS_HTML);
|
2923 |
-
$this->stack[] = $node;
|
2924 |
-
$this->mode = $this->secondary_mode;
|
2925 |
-
$this->emitToken($token);
|
2926 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG) {
|
2927 |
-
static $svg_lookup = [
|
2928 |
-
'altglyph' => 'altGlyph',
|
2929 |
-
'altglyphdef' => 'altGlyphDef',
|
2930 |
-
'altglyphitem' => 'altGlyphItem',
|
2931 |
-
'animatecolor' => 'animateColor',
|
2932 |
-
'animatemotion' => 'animateMotion',
|
2933 |
-
'animatetransform' => 'animateTransform',
|
2934 |
-
'clippath' => 'clipPath',
|
2935 |
-
'feblend' => 'feBlend',
|
2936 |
-
'fecolormatrix' => 'feColorMatrix',
|
2937 |
-
'fecomponenttransfer' => 'feComponentTransfer',
|
2938 |
-
'fecomposite' => 'feComposite',
|
2939 |
-
'feconvolvematrix' => 'feConvolveMatrix',
|
2940 |
-
'fediffuselighting' => 'feDiffuseLighting',
|
2941 |
-
'fedisplacementmap' => 'feDisplacementMap',
|
2942 |
-
'fedistantlight' => 'feDistantLight',
|
2943 |
-
'feflood' => 'feFlood',
|
2944 |
-
'fefunca' => 'feFuncA',
|
2945 |
-
'fefuncb' => 'feFuncB',
|
2946 |
-
'fefuncg' => 'feFuncG',
|
2947 |
-
'fefuncr' => 'feFuncR',
|
2948 |
-
'fegaussianblur' => 'feGaussianBlur',
|
2949 |
-
'feimage' => 'feImage',
|
2950 |
-
'femerge' => 'feMerge',
|
2951 |
-
'femergenode' => 'feMergeNode',
|
2952 |
-
'femorphology' => 'feMorphology',
|
2953 |
-
'feoffset' => 'feOffset',
|
2954 |
-
'fepointlight' => 'fePointLight',
|
2955 |
-
'fespecularlighting' => 'feSpecularLighting',
|
2956 |
-
'fespotlight' => 'feSpotLight',
|
2957 |
-
'fetile' => 'feTile',
|
2958 |
-
'feturbulence' => 'feTurbulence',
|
2959 |
-
'foreignobject' => 'foreignObject',
|
2960 |
-
'glyphref' => 'glyphRef',
|
2961 |
-
'lineargradient' => 'linearGradient',
|
2962 |
-
'radialgradient' => 'radialGradient',
|
2963 |
-
'textpath' => 'textPath',
|
2964 |
-
];
|
2965 |
-
// XDOM
|
2966 |
-
$current = end($this->stack);
|
2967 |
-
if ($current->namespaceURI === self::NS_MATHML) {
|
2968 |
-
$token = $this->adjustMathMLAttributes($token);
|
2969 |
-
}
|
2970 |
-
if ($current->namespaceURI === self::NS_SVG &&
|
2971 |
-
isset($svg_lookup[$token['name']])) {
|
2972 |
-
$token['name'] = $svg_lookup[$token['name']];
|
2973 |
-
}
|
2974 |
-
if ($current->namespaceURI === self::NS_SVG) {
|
2975 |
-
$token = $this->adjustSVGAttributes($token);
|
2976 |
-
}
|
2977 |
-
$token = $this->adjustForeignAttributes($token);
|
2978 |
-
$this->insertForeignElement($token, $current->namespaceURI);
|
2979 |
-
if (isset($token['self-closing'])) {
|
2980 |
-
array_pop($this->stack);
|
2981 |
-
// XERROR: acknowledge self-closing flag
|
2982 |
-
}
|
2983 |
-
}
|
2984 |
-
break;
|
2985 |
-
|
2986 |
-
case self::AFTER_BODY:
|
2987 |
-
/* Handle the token as follows: */
|
2988 |
-
|
2989 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
2990 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
2991 |
-
or U+0020 SPACE */
|
2992 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
2993 |
-
/* Process the token as it would be processed if the insertion mode
|
2994 |
-
was "in body". */
|
2995 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
2996 |
-
|
2997 |
-
/* A comment token */
|
2998 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
2999 |
-
/* Append a Comment node to the first element in the stack of open
|
3000 |
-
elements (the html element), with the data attribute set to the
|
3001 |
-
data given in the comment token. */
|
3002 |
-
// XDOM
|
3003 |
-
$comment = $this->dom->createComment($token['data']);
|
3004 |
-
$this->stack[0]->appendChild($comment);
|
3005 |
-
|
3006 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
3007 |
-
// parse error
|
3008 |
-
|
3009 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
3010 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
3011 |
-
|
3012 |
-
/* An end tag with the tag name "html" */
|
3013 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'html') {
|
3014 |
-
/* If the parser was originally created as part of the HTML
|
3015 |
-
* fragment parsing algorithm, this is a parse error; ignore
|
3016 |
-
* the token. (fragment case) */
|
3017 |
-
$this->ignored = true;
|
3018 |
-
// XERROR: implement this
|
3019 |
-
|
3020 |
-
$this->mode = self::AFTER_AFTER_BODY;
|
3021 |
-
|
3022 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3023 |
-
/* Stop parsing */
|
3024 |
-
|
3025 |
-
/* Anything else */
|
3026 |
-
} else {
|
3027 |
-
/* Parse error. Set the insertion mode to "in body" and reprocess
|
3028 |
-
the token. */
|
3029 |
-
$this->mode = self::IN_BODY;
|
3030 |
-
$this->emitToken($token);
|
3031 |
-
}
|
3032 |
-
break;
|
3033 |
-
|
3034 |
-
case self::IN_FRAMESET:
|
3035 |
-
/* Handle the token as follows: */
|
3036 |
-
|
3037 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
3038 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
3039 |
-
U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
|
3040 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
3041 |
-
/* Append the character to the current node. */
|
3042 |
-
$this->insertText($token['data']);
|
3043 |
-
|
3044 |
-
/* A comment token */
|
3045 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
3046 |
-
/* Append a Comment node to the current node with the data
|
3047 |
-
attribute set to the data given in the comment token. */
|
3048 |
-
$this->insertComment($token['data']);
|
3049 |
-
|
3050 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
3051 |
-
// parse error
|
3052 |
-
|
3053 |
-
/* A start tag with the tag name "frameset" */
|
3054 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
3055 |
-
$token['name'] === 'frameset') {
|
3056 |
-
$this->insertElement($token);
|
3057 |
-
|
3058 |
-
/* An end tag with the tag name "frameset" */
|
3059 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
3060 |
-
$token['name'] === 'frameset') {
|
3061 |
-
/* If the current node is the root html element, then this is a
|
3062 |
-
parse error; ignore the token. (fragment case) */
|
3063 |
-
if (end($this->stack)->tagName === 'html') {
|
3064 |
-
$this->ignored = true;
|
3065 |
-
// Parse error
|
3066 |
-
|
3067 |
-
} else {
|
3068 |
-
/* Otherwise, pop the current node from the stack of open
|
3069 |
-
elements. */
|
3070 |
-
array_pop($this->stack);
|
3071 |
-
|
3072 |
-
/* If the parser was not originally created as part of the HTML
|
3073 |
-
* fragment parsing algorithm (fragment case), and the current
|
3074 |
-
* node is no longer a frameset element, then switch the
|
3075 |
-
* insertion mode to "after frameset". */
|
3076 |
-
$this->mode = self::AFTER_FRAMESET;
|
3077 |
-
}
|
3078 |
-
|
3079 |
-
/* A start tag with the tag name "frame" */
|
3080 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
3081 |
-
$token['name'] === 'frame') {
|
3082 |
-
/* Insert an HTML element for the token. */
|
3083 |
-
$this->insertElement($token);
|
3084 |
-
|
3085 |
-
/* Immediately pop the current node off the stack of open elements. */
|
3086 |
-
array_pop($this->stack);
|
3087 |
-
|
3088 |
-
// XERROR: Acknowledge the token's self-closing flag, if it is set.
|
3089 |
-
|
3090 |
-
/* A start tag with the tag name "noframes" */
|
3091 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
3092 |
-
$token['name'] === 'noframes') {
|
3093 |
-
/* Process the token using the rules for the "in head" insertion mode. */
|
3094 |
-
$this->processwithRulesFor($token, self::IN_HEAD);
|
3095 |
-
|
3096 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3097 |
-
// XERROR: If the current node is not the root html element, then this is a parse error.
|
3098 |
-
/* Stop parsing */
|
3099 |
-
/* Anything else */
|
3100 |
-
} else {
|
3101 |
-
/* Parse error. Ignore the token. */
|
3102 |
-
$this->ignored = true;
|
3103 |
-
}
|
3104 |
-
break;
|
3105 |
-
|
3106 |
-
case self::AFTER_FRAMESET:
|
3107 |
-
/* Handle the token as follows: */
|
3108 |
-
|
3109 |
-
/* A character token that is one of one of U+0009 CHARACTER TABULATION,
|
3110 |
-
U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
|
3111 |
-
U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
|
3112 |
-
if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
|
3113 |
-
/* Append the character to the current node. */
|
3114 |
-
$this->insertText($token['data']);
|
3115 |
-
|
3116 |
-
/* A comment token */
|
3117 |
-
} elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
3118 |
-
/* Append a Comment node to the current node with the data
|
3119 |
-
attribute set to the data given in the comment token. */
|
3120 |
-
$this->insertComment($token['data']);
|
3121 |
-
|
3122 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
|
3123 |
-
// parse error
|
3124 |
-
|
3125 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
|
3126 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
3127 |
-
|
3128 |
-
/* An end tag with the tag name "html" */
|
3129 |
-
} elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
|
3130 |
-
$token['name'] === 'html') {
|
3131 |
-
$this->mode = self::AFTER_AFTER_FRAMESET;
|
3132 |
-
|
3133 |
-
/* A start tag with the tag name "noframes" */
|
3134 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
|
3135 |
-
$token['name'] === 'noframes') {
|
3136 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
3137 |
-
|
3138 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3139 |
-
/* Stop parsing */
|
3140 |
-
|
3141 |
-
/* Anything else */
|
3142 |
-
} else {
|
3143 |
-
/* Parse error. Ignore the token. */
|
3144 |
-
$this->ignored = true;
|
3145 |
-
}
|
3146 |
-
break;
|
3147 |
-
|
3148 |
-
case self::AFTER_AFTER_BODY:
|
3149 |
-
/* A comment token */
|
3150 |
-
if ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
3151 |
-
/* Append a Comment node to the Document object with the data
|
3152 |
-
attribute set to the data given in the comment token. */
|
3153 |
-
// XDOM
|
3154 |
-
$comment = $this->dom->createComment($token['data']);
|
3155 |
-
$this->dom->appendChild($comment);
|
3156 |
-
|
3157 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE ||
|
3158 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER ||
|
3159 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) {
|
3160 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
3161 |
-
|
3162 |
-
/* An end-of-file token */
|
3163 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3164 |
-
/* OMG DONE!! */
|
3165 |
-
} else {
|
3166 |
-
// parse error
|
3167 |
-
$this->mode = self::IN_BODY;
|
3168 |
-
$this->emitToken($token);
|
3169 |
-
}
|
3170 |
-
break;
|
3171 |
-
|
3172 |
-
case self::AFTER_AFTER_FRAMESET:
|
3173 |
-
/* A comment token */
|
3174 |
-
if ($token['type'] === HTML5_Tokenizer::COMMENT) {
|
3175 |
-
/* Append a Comment node to the Document object with the data
|
3176 |
-
attribute set to the data given in the comment token. */
|
3177 |
-
// XDOM
|
3178 |
-
$comment = $this->dom->createComment($token['data']);
|
3179 |
-
$this->dom->appendChild($comment);
|
3180 |
-
} elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE ||
|
3181 |
-
$token['type'] === HTML5_Tokenizer::SPACECHARACTER ||
|
3182 |
-
($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) {
|
3183 |
-
$this->processWithRulesFor($token, self::IN_BODY);
|
3184 |
-
|
3185 |
-
/* An end-of-file token */
|
3186 |
-
} elseif ($token['type'] === HTML5_Tokenizer::EOF) {
|
3187 |
-
/* OMG DONE!! */
|
3188 |
-
} elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'nofrmaes') {
|
3189 |
-
$this->processWithRulesFor($token, self::IN_HEAD);
|
3190 |
-
} else {
|
3191 |
-
// parse error
|
3192 |
-
}
|
3193 |
-
break;
|
3194 |
-
}
|
3195 |
-
}
|
3196 |
-
|
3197 |
-
private function insertElement($token, $append = true) {
|
3198 |
-
$el = $this->dom->createElementNS(self::NS_HTML, $token['name']);
|
3199 |
-
|
3200 |
-
if (!empty($token['attr'])) {
|
3201 |
-
foreach ($token['attr'] as $attr) {
|
3202 |
-
if (!$el->hasAttribute($attr['name']) && preg_match("/^[a-zA-Z_:]/", $attr['name'])) {
|
3203 |
-
$el->setAttribute($attr['name'], $attr['value']);
|
3204 |
-
}
|
3205 |
-
}
|
3206 |
-
}
|
3207 |
-
if ($append) {
|
3208 |
-
$this->appendToRealParent($el);
|
3209 |
-
$this->stack[] = $el;
|
3210 |
-
}
|
3211 |
-
|
3212 |
-
return $el;
|
3213 |
-
}
|
3214 |
-
|
3215 |
-
/**
|
3216 |
-
* @param $data
|
3217 |
-
*/
|
3218 |
-
private function insertText($data) {
|
3219 |
-
if ($data === '') {
|
3220 |
-
return;
|
3221 |
-
}
|
3222 |
-
if ($this->ignore_lf_token) {
|
3223 |
-
if ($data[0] === "\n") {
|
3224 |
-
$data = substr($data, 1);
|
3225 |
-
if ($data === false) {
|
3226 |
-
return;
|
3227 |
-
}
|
3228 |
-
}
|
3229 |
-
}
|
3230 |
-
$text = $this->dom->createTextNode($data);
|
3231 |
-
$this->appendToRealParent($text);
|
3232 |
-
}
|
3233 |
-
|
3234 |
-
/**
|
3235 |
-
* @param $data
|
3236 |
-
*/
|
3237 |
-
private function insertComment($data) {
|
3238 |
-
$comment = $this->dom->createComment($data);
|
3239 |
-
$this->appendToRealParent($comment);
|
3240 |
-
}
|
3241 |
-
|
3242 |
-
/**
|
3243 |
-
* @param $node
|
3244 |
-
*/
|
3245 |
-
private function appendToRealParent($node) {
|
3246 |
-
// this is only for the foster_parent case
|
3247 |
-
/* If the current node is a table, tbody, tfoot, thead, or tr
|
3248 |
-
element, then, whenever a node would be inserted into the current
|
3249 |
-
node, it must instead be inserted into the foster parent element. */
|
3250 |
-
if (
|
3251 |
-
!$this->foster_parent ||
|
3252 |
-
!in_array(
|
3253 |
-
end($this->stack)->tagName,
|
3254 |
-
['table', 'tbody', 'tfoot', 'thead', 'tr']
|
3255 |
-
)
|
3256 |
-
) {
|
3257 |
-
end($this->stack)->appendChild($node);
|
3258 |
-
} else {
|
3259 |
-
$this->fosterParent($node);
|
3260 |
-
}
|
3261 |
-
}
|
3262 |
-
|
3263 |
-
/**
|
3264 |
-
* @param $el
|
3265 |
-
* @param int $scope
|
3266 |
-
* @return bool|null
|
3267 |
-
*/
|
3268 |
-
private function elementInScope($el, $scope = self::SCOPE) {
|
3269 |
-
if (is_array($el)) {
|
3270 |
-
foreach($el as $element) {
|
3271 |
-
if ($this->elementInScope($element, $scope)) {
|
3272 |
-
return true;
|
3273 |
-
}
|
3274 |
-
}
|
3275 |
-
|
3276 |
-
return false;
|
3277 |
-
}
|
3278 |
-
|
3279 |
-
$leng = count($this->stack);
|
3280 |
-
|
3281 |
-
for ($n = 0; $n < $leng; $n++) {
|
3282 |
-
/* 1. Initialise node to be the current node (the bottommost node of
|
3283 |
-
the stack). */
|
3284 |
-
$node = $this->stack[$leng - 1 - $n];
|
3285 |
-
|
3286 |
-
if ($node->tagName === $el) {
|
3287 |
-
/* 2. If node is the target node, terminate in a match state. */
|
3288 |
-
return true;
|
3289 |
-
|
3290 |
-
// We've expanded the logic for these states a little differently;
|
3291 |
-
// Hixie's refactoring into "specific scope" is more general, but
|
3292 |
-
// this "gets the job done"
|
3293 |
-
|
3294 |
-
// these are the common states for all scopes
|
3295 |
-
} elseif ($node->tagName === 'table' || $node->tagName === 'html') {
|
3296 |
-
return false;
|
3297 |
-
|
3298 |
-
// these are valid for "in scope" and "in list item scope"
|
3299 |
-
} elseif ($scope !== self::SCOPE_TABLE &&
|
3300 |
-
(in_array($node->tagName, ['applet', 'caption', 'td',
|
3301 |
-
'th', 'button', 'marquee', 'object']) ||
|
3302 |
-
$node->tagName === 'foreignObject' && $node->namespaceURI === self::NS_SVG)) {
|
3303 |
-
return false;
|
3304 |
-
|
3305 |
-
|
3306 |
-
// these are valid for "in list item scope"
|
3307 |
-
} elseif ($scope === self::SCOPE_LISTITEM && in_array($node->tagName, ['ol', 'ul'])) {
|
3308 |
-
return false;
|
3309 |
-
}
|
3310 |
-
|
3311 |
-
/* Otherwise, set node to the previous entry in the stack of open
|
3312 |
-
elements and return to step 2. (This will never fail, since the loop
|
3313 |
-
will always terminate in the previous step if the top of the stack
|
3314 |
-
is reached.) */
|
3315 |
-
}
|
3316 |
-
|
3317 |
-
// To fix warning. This never happens or should return true/false
|
3318 |
-
return null;
|
3319 |
-
}
|
3320 |
-
|
3321 |
-
/**
|
3322 |
-
* @return bool
|
3323 |
-
*/
|
3324 |
-
private function reconstructActiveFormattingElements() {
|
3325 |
-
/* 1. If there are no entries in the list of active formatting elements,
|
3326 |
-
then there is nothing to reconstruct; stop this algorithm. */
|
3327 |
-
$formatting_elements = count($this->a_formatting);
|
3328 |
-
|
3329 |
-
if ($formatting_elements === 0) {
|
3330 |
-
return false;
|
3331 |
-
}
|
3332 |
-
|
3333 |
-
/* 3. Let entry be the last (most recently added) element in the list
|
3334 |
-
of active formatting elements. */
|
3335 |
-
$entry = end($this->a_formatting);
|
3336 |
-
|
3337 |
-
/* 2. If the last (most recently added) entry in the list of active
|
3338 |
-
formatting elements is a marker, or if it is an element that is in the
|
3339 |
-
stack of open elements, then there is nothing to reconstruct; stop this
|
3340 |
-
algorithm. */
|
3341 |
-
if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
|
3342 |
-
return false;
|
3343 |
-
}
|
3344 |
-
|
3345 |
-
for ($a = $formatting_elements - 1; $a >= 0; true) {
|
3346 |
-
/* 4. If there are no entries before entry in the list of active
|
3347 |
-
formatting elements, then jump to step 8. */
|
3348 |
-
if ($a === 0) {
|
3349 |
-
$step_seven = false;
|
3350 |
-
break;
|
3351 |
-
}
|
3352 |
-
|
3353 |
-
/* 5. Let entry be the entry one earlier than entry in the list of
|
3354 |
-
active formatting elements. */
|
3355 |
-
$a--;
|
3356 |
-
$entry = $this->a_formatting[$a];
|
3357 |
-
|
3358 |
-
/* 6. If entry is neither a marker nor an element that is also in
|
3359 |
-
thetack of open elements, go to step 4. */
|
3360 |
-
if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
|
3361 |
-
break;
|
3362 |
-
}
|
3363 |
-
}
|
3364 |
-
|
3365 |
-
while (true) {
|
3366 |
-
/* 7. Let entry be the element one later than entry in the list of
|
3367 |
-
active formatting elements. */
|
3368 |
-
if (isset($step_seven) && $step_seven === true) {
|
3369 |
-
$a++;
|
3370 |
-
$entry = $this->a_formatting[$a];
|
3371 |
-
}
|
3372 |
-
|
3373 |
-
/* 8. Perform a shallow clone of the element entry to obtain clone. */
|
3374 |
-
$clone = $entry->cloneNode();
|
3375 |
-
|
3376 |
-
/* 9. Append clone to the current node and push it onto the stack
|
3377 |
-
of open elements so that it is the new current node. */
|
3378 |
-
$this->appendToRealParent($clone);
|
3379 |
-
$this->stack[] = $clone;
|
3380 |
-
|
3381 |
-
/* 10. Replace the entry for entry in the list with an entry for
|
3382 |
-
clone. */
|
3383 |
-
$this->a_formatting[$a] = $clone;
|
3384 |
-
|
3385 |
-
/* 11. If the entry for clone in the list of active formatting
|
3386 |
-
elements is not the last entry in the list, return to step 7. */
|
3387 |
-
if (end($this->a_formatting) !== $clone) {
|
3388 |
-
$step_seven = true;
|
3389 |
-
} else {
|
3390 |
-
break;
|
3391 |
-
}
|
3392 |
-
}
|
3393 |
-
|
3394 |
-
// Return value not in use ATM. Would just make sense to also return true here.
|
3395 |
-
return true;
|
3396 |
-
}
|
3397 |
-
|
3398 |
-
/**
|
3399 |
-
*
|
3400 |
-
*/
|
3401 |
-
private function clearTheActiveFormattingElementsUpToTheLastMarker() {
|
3402 |
-
/* When the steps below require the UA to clear the list of active
|
3403 |
-
formatting elements up to the last marker, the UA must perform the
|
3404 |
-
following steps: */
|
3405 |
-
|
3406 |
-
while (true) {
|
3407 |
-
/* 1. Let entry be the last (most recently added) entry in the list
|
3408 |
-
of active formatting elements. */
|
3409 |
-
$entry = end($this->a_formatting);
|
3410 |
-
|
3411 |
-
/* 2. Remove entry from the list of active formatting elements. */
|
3412 |
-
array_pop($this->a_formatting);
|
3413 |
-
|
3414 |
-
/* 3. If entry was a marker, then stop the algorithm at this point.
|
3415 |
-
The list has been cleared up to the last marker. */
|
3416 |
-
if ($entry === self::MARKER) {
|
3417 |
-
break;
|
3418 |
-
}
|
3419 |
-
}
|
3420 |
-
}
|
3421 |
-
|
3422 |
-
/**
|
3423 |
-
* @param array $exclude
|
3424 |
-
*/
|
3425 |
-
private function generateImpliedEndTags($exclude = []) {
|
3426 |
-
/* When the steps below require the UA to generate implied end tags,
|
3427 |
-
* then, while the current node is a dc element, a dd element, a ds
|
3428 |
-
* element, a dt element, an li element, an option element, an optgroup
|
3429 |
-
* element, a p element, an rp element, or an rt element, the UA must
|
3430 |
-
* pop the current node off the stack of open elements. */
|
3431 |
-
$node = end($this->stack);
|
3432 |
-
$elements = array_diff(['dc', 'dd', 'ds', 'dt', 'li', 'p', 'td', 'th', 'tr'], $exclude);
|
3433 |
-
|
3434 |
-
while (in_array(end($this->stack)->tagName, $elements)) {
|
3435 |
-
array_pop($this->stack);
|
3436 |
-
}
|
3437 |
-
}
|
3438 |
-
|
3439 |
-
/**
|
3440 |
-
* @param $node
|
3441 |
-
* @return int
|
3442 |
-
*/
|
3443 |
-
private function getElementCategory($node) {
|
3444 |
-
if (!is_object($node)) {
|
3445 |
-
debug_print_backtrace();
|
3446 |
-
}
|
3447 |
-
$name = $node->tagName;
|
3448 |
-
if (in_array($name, $this->special)) {
|
3449 |
-
return self::SPECIAL;
|
3450 |
-
} elseif (in_array($name, $this->scoping)) {
|
3451 |
-
return self::SCOPING;
|
3452 |
-
} elseif (in_array($name, $this->formatting)) {
|
3453 |
-
return self::FORMATTING;
|
3454 |
-
} else {
|
3455 |
-
return self::PHRASING;
|
3456 |
-
}
|
3457 |
-
}
|
3458 |
-
|
3459 |
-
/**
|
3460 |
-
* @param $elements
|
3461 |
-
*/
|
3462 |
-
private function clearStackToTableContext($elements) {
|
3463 |
-
/* When the steps above require the UA to clear the stack back to a
|
3464 |
-
table context, it means that the UA must, while the current node is not
|
3465 |
-
a table element or an html element, pop elements from the stack of open
|
3466 |
-
elements. */
|
3467 |
-
while (true) {
|
3468 |
-
$name = end($this->stack)->tagName;
|
3469 |
-
|
3470 |
-
if (in_array($name, $elements)) {
|
3471 |
-
break;
|
3472 |
-
} else {
|
3473 |
-
array_pop($this->stack);
|
3474 |
-
}
|
3475 |
-
}
|
3476 |
-
}
|
3477 |
-
|
3478 |
-
/**
|
3479 |
-
* @param null $context
|
3480 |
-
*/
|
3481 |
-
private function resetInsertionMode($context = null) {
|
3482 |
-
/* 1. Let last be false. */
|
3483 |
-
$last = false;
|
3484 |
-
$leng = count($this->stack);
|
3485 |
-
|
3486 |
-
for ($n = $leng - 1; $n >= 0; $n--) {
|
3487 |
-
/* 2. Let node be the last node in the stack of open elements. */
|
3488 |
-
$node = $this->stack[$n];
|
3489 |
-
|
3490 |
-
/* 3. If node is the first node in the stack of open elements, then
|
3491 |
-
* set last to true and set node to the context element. (fragment
|
3492 |
-
* case) */
|
3493 |
-
if ($this->stack[0]->isSameNode($node)) {
|
3494 |
-
$last = true;
|
3495 |
-
$node = $context;
|
3496 |
-
}
|
3497 |
-
|
3498 |
-
/* 4. If node is a select element, then switch the insertion mode to
|
3499 |
-
"in select" and abort these steps. (fragment case) */
|
3500 |
-
if ($node->tagName === 'select') {
|
3501 |
-
$this->mode = self::IN_SELECT;
|
3502 |
-
break;
|
3503 |
-
|
3504 |
-
/* 5. If node is a td or th element, then switch the insertion mode
|
3505 |
-
to "in cell" and abort these steps. */
|
3506 |
-
} elseif ($node->tagName === 'td' || $node->nodeName === 'th') {
|
3507 |
-
$this->mode = self::IN_CELL;
|
3508 |
-
break;
|
3509 |
-
|
3510 |
-
/* 6. If node is a tr element, then switch the insertion mode to
|
3511 |
-
"in row" and abort these steps. */
|
3512 |
-
} elseif ($node->tagName === 'tr') {
|
3513 |
-
$this->mode = self::IN_ROW;
|
3514 |
-
break;
|
3515 |
-
|
3516 |
-
/* 7. If node is a tbody, thead, or tfoot element, then switch the
|
3517 |
-
insertion mode to "in table body" and abort these steps. */
|
3518 |
-
} elseif (in_array($node->tagName, ['tbody', 'thead', 'tfoot'])) {
|
3519 |
-
$this->mode = self::IN_TABLE_BODY;
|
3520 |
-
break;
|
3521 |
-
|
3522 |
-
/* 8. If node is a caption element, then switch the insertion mode
|
3523 |
-
to "in caption" and abort these steps. */
|
3524 |
-
} elseif ($node->tagName === 'caption') {
|
3525 |
-
$this->mode = self::IN_CAPTION;
|
3526 |
-
break;
|
3527 |
-
|
3528 |
-
/* 9. If node is a colgroup element, then switch the insertion mode
|
3529 |
-
to "in column group" and abort these steps. (innerHTML case) */
|
3530 |
-
} elseif ($node->tagName === 'colgroup') {
|
3531 |
-
$this->mode = self::IN_COLUMN_GROUP;
|
3532 |
-
break;
|
3533 |
-
|
3534 |
-
/* 10. If node is a table element, then switch the insertion mode
|
3535 |
-
to "in table" and abort these steps. */
|
3536 |
-
} elseif ($node->tagName === 'table') {
|
3537 |
-
$this->mode = self::IN_TABLE;
|
3538 |
-
break;
|
3539 |
-
|
3540 |
-
/* 11. If node is an element from the MathML namespace or the SVG
|
3541 |
-
* namespace, then switch the insertion mode to "in foreign
|
3542 |
-
* content", let the secondary insertion mode be "in body", and
|
3543 |
-
* abort these steps. */
|
3544 |
-
} elseif ($node->namespaceURI === self::NS_SVG ||
|
3545 |
-
$node->namespaceURI === self::NS_MATHML) {
|
3546 |
-
$this->mode = self::IN_FOREIGN_CONTENT;
|
3547 |
-
$this->secondary_mode = self::IN_BODY;
|
3548 |
-
break;
|
3549 |
-
|
3550 |
-
/* 12. If node is a head element, then switch the insertion mode
|
3551 |
-
to "in body" ("in body"! not "in head"!) and abort these steps.
|
3552 |
-
(fragment case) */
|
3553 |
-
} elseif ($node->tagName === 'head') {
|
3554 |
-
$this->mode = self::IN_BODY;
|
3555 |
-
break;
|
3556 |
-
|
3557 |
-
/* 13. If node is a body element, then switch the insertion mode to
|
3558 |
-
"in body" and abort these steps. */
|
3559 |
-
} elseif ($node->tagName === 'body') {
|
3560 |
-
$this->mode = self::IN_BODY;
|
3561 |
-
break;
|
3562 |
-
|
3563 |
-
/* 14. If node is a frameset element, then switch the insertion
|
3564 |
-
mode to "in frameset" and abort these steps. (fragment case) */
|
3565 |
-
} elseif ($node->tagName === 'frameset') {
|
3566 |
-
$this->mode = self::IN_FRAMESET;
|
3567 |
-
break;
|
3568 |
-
|
3569 |
-
/* 15. If node is an html element, then: if the head element
|
3570 |
-
pointer is null, switch the insertion mode to "before head",
|
3571 |
-
otherwise, switch the insertion mode to "after head". In either
|
3572 |
-
case, abort these steps. (fragment case) */
|
3573 |
-
} elseif ($node->tagName === 'html') {
|
3574 |
-
$this->mode = ($this->head_pointer === null)
|
3575 |
-
? self::BEFORE_HEAD
|
3576 |
-
: self::AFTER_HEAD;
|
3577 |
-
|
3578 |
-
break;
|
3579 |
-
|
3580 |
-
/* 16. If last is true, then set the insertion mode to "in body"
|
3581 |
-
and abort these steps. (fragment case) */
|
3582 |
-
} elseif ($last) {
|
3583 |
-
$this->mode = self::IN_BODY;
|
3584 |
-
break;
|
3585 |
-
}
|
3586 |
-
}
|
3587 |
-
}
|
3588 |
-
|
3589 |
-
/**
|
3590 |
-
*
|
3591 |
-
*/
|
3592 |
-
private function closeCell() {
|
3593 |
-
/* If the stack of open elements has a td or th element in table scope,
|
3594 |
-
then act as if an end tag token with that tag name had been seen. */
|
3595 |
-
foreach (['td', 'th'] as $cell) {
|
3596 |
-
if ($this->elementInScope($cell, self::SCOPE_TABLE)) {
|
3597 |
-
$this->emitToken([
|
3598 |
-
'name' => $cell,
|
3599 |
-
'type' => HTML5_Tokenizer::ENDTAG
|
3600 |
-
]);
|
3601 |
-
|
3602 |
-
break;
|
3603 |
-
}
|
3604 |
-
}
|
3605 |
-
}
|
3606 |
-
|
3607 |
-
/**
|
3608 |
-
* @param $token
|
3609 |
-
* @param $mode
|
3610 |
-
*/
|
3611 |
-
private function processWithRulesFor($token, $mode) {
|
3612 |
-
/* "using the rules for the m insertion mode", where m is one of these
|
3613 |
-
* modes, the user agent must use the rules described under the m
|
3614 |
-
* insertion mode's section, but must leave the insertion mode
|
3615 |
-
* unchanged unless the rules in m themselves switch the insertion mode
|
3616 |
-
* to a new value. */
|
3617 |
-
$this->emitToken($token, $mode);
|
3618 |
-
}
|
3619 |
-
|
3620 |
-
/**
|
3621 |
-
* @param $token
|
3622 |
-
*/
|
3623 |
-
private function insertCDATAElement($token) {
|
3624 |
-
$this->insertElement($token);
|
3625 |
-
$this->original_mode = $this->mode;
|
3626 |
-
$this->mode = self::IN_CDATA_RCDATA;
|
3627 |
-
$this->content_model = HTML5_Tokenizer::CDATA;
|
3628 |
-
}
|
3629 |
-
|
3630 |
-
/**
|
3631 |
-
* @param $token
|
3632 |
-
*/
|
3633 |
-
private function insertRCDATAElement($token) {
|
3634 |
-
$this->insertElement($token);
|
3635 |
-
$this->original_mode = $this->mode;
|
3636 |
-
$this->mode = self::IN_CDATA_RCDATA;
|
3637 |
-
$this->content_model = HTML5_Tokenizer::RCDATA;
|
3638 |
-
}
|
3639 |
-
|
3640 |
-
/**
|
3641 |
-
* @param $token
|
3642 |
-
* @param $key
|
3643 |
-
* @return bool
|
3644 |
-
*/
|
3645 |
-
private function getAttr($token, $key) {
|
3646 |
-
if (!isset($token['attr'])) {
|
3647 |
-
return false;
|
3648 |
-
}
|
3649 |
-
$ret = false;
|
3650 |
-
foreach ($token['attr'] as $keypair) {
|
3651 |
-
if ($keypair['name'] === $key) {
|
3652 |
-
$ret = $keypair['value'];
|
3653 |
-
}
|
3654 |
-
}
|
3655 |
-
return $ret;
|
3656 |
-
}
|
3657 |
-
|
3658 |
-
/**
|
3659 |
-
* @return mixed
|
3660 |
-
*/
|
3661 |
-
private function getCurrentTable() {
|
3662 |
-
/* The current table is the last table element in the stack of open
|
3663 |
-
* elements, if there is one. If there is no table element in the stack
|
3664 |
-
* of open elements (fragment case), then the current table is the
|
3665 |
-
* first element in the stack of open elements (the html element). */
|
3666 |
-
for ($i = count($this->stack) - 1; $i >= 0; $i--) {
|
3667 |
-
if ($this->stack[$i]->tagName === 'table') {
|
3668 |
-
return $this->stack[$i];
|
3669 |
-
}
|
3670 |
-
}
|
3671 |
-
return $this->stack[0];
|
3672 |
-
}
|
3673 |
-
|
3674 |
-
/**
|
3675 |
-
* @return mixed
|
3676 |
-
*/
|
3677 |
-
private function getFosterParent() {
|
3678 |
-
/* The foster parent element is the parent element of the last
|
3679 |
-
table element in the stack of open elements, if there is a
|
3680 |
-
table element and it has such a parent element. If there is no
|
3681 |
-
table element in the stack of open elements (innerHTML case),
|
3682 |
-
then the foster parent element is the first element in the
|
3683 |
-
stack of open elements (the html element). Otherwise, if there
|
3684 |
-
is a table element in the stack of open elements, but the last
|
3685 |
-
table element in the stack of open elements has no parent, or
|
3686 |
-
its parent node is not an element, then the foster parent
|
3687 |
-
element is the element before the last table element in the
|
3688 |
-
stack of open elements. */
|
3689 |
-
for ($n = count($this->stack) - 1; $n >= 0; $n--) {
|
3690 |
-
if ($this->stack[$n]->tagName === 'table') {
|
3691 |
-
$table = $this->stack[$n];
|
3692 |
-
break;
|
3693 |
-
}
|
3694 |
-
}
|
3695 |
-
|
3696 |
-
if (isset($table) && $table->parentNode !== null) {
|
3697 |
-
return $table->parentNode;
|
3698 |
-
|
3699 |
-
} elseif (!isset($table)) {
|
3700 |
-
return $this->stack[0];
|
3701 |
-
|
3702 |
-
} elseif (isset($table) && ($table->parentNode === null ||
|
3703 |
-
$table->parentNode->nodeType !== XML_ELEMENT_NODE)) {
|
3704 |
-
return $this->stack[$n - 1];
|
3705 |
-
}
|
3706 |
-
|
3707 |
-
return null;
|
3708 |
-
}
|
3709 |
-
|
3710 |
-
/**
|
3711 |
-
* @param $node
|
3712 |
-
*/
|
3713 |
-
public function fosterParent($node) {
|
3714 |
-
$foster_parent = $this->getFosterParent();
|
3715 |
-
$table = $this->getCurrentTable(); // almost equivalent to last table element, except it can be html
|
3716 |
-
/* When a node node is to be foster parented, the node node must be
|
3717 |
-
* be inserted into the foster parent element. */
|
3718 |
-
/* If the foster parent element is the parent element of the last table
|
3719 |
-
* element in the stack of open elements, then node must be inserted
|
3720 |
-
* immediately before the last table element in the stack of open
|
3721 |
-
* elements in the foster parent element; otherwise, node must be
|
3722 |
-
* appended to the foster parent element. */
|
3723 |
-
if ($table->tagName === 'table' && $table->parentNode->isSameNode($foster_parent)) {
|
3724 |
-
$foster_parent->insertBefore($node, $table);
|
3725 |
-
} else {
|
3726 |
-
$foster_parent->appendChild($node);
|
3727 |
-
}
|
3728 |
-
}
|
3729 |
-
|
3730 |
-
/**
|
3731 |
-
* For debugging, prints the stack
|
3732 |
-
*/
|
3733 |
-
private function printStack() {
|
3734 |
-
$names = [];
|
3735 |
-
foreach ($this->stack as $i => $element) {
|
3736 |
-
$names[] = $element->tagName;
|
3737 |
-
}
|
3738 |
-
echo " -> stack [" . implode(', ', $names) . "]\n";
|
3739 |
-
}
|
3740 |
-
|
3741 |
-
/**
|
3742 |
-
* For debugging, prints active formatting elements
|
3743 |
-
*/
|
3744 |
-
private function printActiveFormattingElements() {
|
3745 |
-
if (!$this->a_formatting) {
|
3746 |
-
return;
|
3747 |
-
}
|
3748 |
-
$names = [];
|
3749 |
-
foreach ($this->a_formatting as $node) {
|
3750 |
-
if ($node === self::MARKER) {
|
3751 |
-
$names[] = 'MARKER';
|
3752 |
-
} else {
|
3753 |
-
$names[] = $node->tagName;
|
3754 |
-
}
|
3755 |
-
}
|
3756 |
-
echo " -> active formatting [" . implode(', ', $names) . "]\n";
|
3757 |
-
}
|
3758 |
-
|
3759 |
-
/**
|
3760 |
-
* @return bool
|
3761 |
-
*/
|
3762 |
-
public function currentTableIsTainted() {
|
3763 |
-
return !empty($this->getCurrentTable()->tainted);
|
3764 |
-
}
|
3765 |
-
|
3766 |
-
/**
|
3767 |
-
* Sets up the tree constructor for building a fragment.
|
3768 |
-
*
|
3769 |
-
* @param null $context
|
3770 |
-
*/
|
3771 |
-
public function setupContext($context = null) {
|
3772 |
-
$this->fragment = true;
|
3773 |
-
if ($context) {
|
3774 |
-
$context = $this->dom->createElementNS(self::NS_HTML, $context);
|
3775 |
-
/* 4.1. Set the HTML parser's tokenization stage's content model
|
3776 |
-
* flag according to the context element, as follows: */
|
3777 |
-
switch ($context->tagName) {
|
3778 |
-
case 'title': case 'textarea':
|
3779 |
-
$this->content_model = HTML5_Tokenizer::RCDATA;
|
3780 |
-
break;
|
3781 |
-
case 'style': case 'script': case 'xmp': case 'iframe':
|
3782 |
-
case 'noembed': case 'noframes':
|
3783 |
-
$this->content_model = HTML5_Tokenizer::CDATA;
|
3784 |
-
break;
|
3785 |
-
case 'noscript':
|
3786 |
-
// XSCRIPT: assuming scripting is enabled
|
3787 |
-
$this->content_model = HTML5_Tokenizer::CDATA;
|
3788 |
-
break;
|
3789 |
-
case 'plaintext':
|
3790 |
-
$this->content_model = HTML5_Tokenizer::PLAINTEXT;
|
3791 |
-
break;
|
3792 |
-
}
|
3793 |
-
/* 4.2. Let root be a new html element with no attributes. */
|
3794 |
-
$root = $this->dom->createElementNS(self::NS_HTML, 'html');
|
3795 |
-
$this->root = $root;
|
3796 |
-
/* 4.3 Append the element root to the Document node created above. */
|
3797 |
-
$this->dom->appendChild($root);
|
3798 |
-
/* 4.4 Set up the parser's stack of open elements so that it
|
3799 |
-
* contains just the single element root. */
|
3800 |
-
$this->stack = [$root];
|
3801 |
-
/* 4.5 Reset the parser's insertion mode appropriately. */
|
3802 |
-
$this->resetInsertionMode($context);
|
3803 |
-
/* 4.6 Set the parser's form element pointer to the nearest node
|
3804 |
-
* to the context element that is a form element (going straight up
|
3805 |
-
* the ancestor chain, and including the element itself, if it is a
|
3806 |
-
* form element), or, if there is no such form element, to null. */
|
3807 |
-
$node = $context;
|
3808 |
-
do {
|
3809 |
-
if ($node->tagName === 'form') {
|
3810 |
-
$this->form_pointer = $node;
|
3811 |
-
break;
|
3812 |
-
}
|
3813 |
-
} while ($node = $node->parentNode);
|
3814 |
-
}
|
3815 |
-
}
|
3816 |
-
|
3817 |
-
/**
|
3818 |
-
* @param $token
|
3819 |
-
* @return mixed
|
3820 |
-
*/
|
3821 |
-
public function adjustMathMLAttributes($token) {
|
3822 |
-
foreach ($token['attr'] as &$kp) {
|
3823 |
-
if ($kp['name'] === 'definitionurl') {
|
3824 |
-
$kp['name'] = 'definitionURL';
|
3825 |
-
}
|
3826 |
-
}
|
3827 |
-
return $token;
|
3828 |
-
}
|
3829 |
-
|
3830 |
-
/**
|
3831 |
-
* @param $token
|
3832 |
-
* @return mixed
|
3833 |
-
*/
|
3834 |
-
public function adjustSVGAttributes($token) {
|
3835 |
-
static $lookup = [
|
3836 |
-
'attributename' => 'attributeName',
|
3837 |
-
'attributetype' => 'attributeType',
|
3838 |
-
'basefrequency' => 'baseFrequency',
|
3839 |
-
'baseprofile' => 'baseProfile',
|
3840 |
-
'calcmode' => 'calcMode',
|
3841 |
-
'clippathunits' => 'clipPathUnits',
|
3842 |
-
'contentscripttype' => 'contentScriptType',
|
3843 |
-
'contentstyletype' => 'contentStyleType',
|
3844 |
-
'diffuseconstant' => 'diffuseConstant',
|
3845 |
-
'edgemode' => 'edgeMode',
|
3846 |
-
'externalresourcesrequired' => 'externalResourcesRequired',
|
3847 |
-
'filterres' => 'filterRes',
|
3848 |
-
'filterunits' => 'filterUnits',
|
3849 |
-
'glyphref' => 'glyphRef',
|
3850 |
-
'gradienttransform' => 'gradientTransform',
|
3851 |
-
'gradientunits' => 'gradientUnits',
|
3852 |
-
'kernelmatrix' => 'kernelMatrix',
|
3853 |
-
'kernelunitlength' => 'kernelUnitLength',
|
3854 |
-
'keypoints' => 'keyPoints',
|
3855 |
-
'keysplines' => 'keySplines',
|
3856 |
-
'keytimes' => 'keyTimes',
|
3857 |
-
'lengthadjust' => 'lengthAdjust',
|
3858 |
-
'limitingconeangle' => 'limitingConeAngle',
|
3859 |
-
'markerheight' => 'markerHeight',
|
3860 |
-
'markerunits' => 'markerUnits',
|
3861 |
-
'markerwidth' => 'markerWidth',
|
3862 |
-
'maskcontentunits' => 'maskContentUnits',
|
3863 |
-
'maskunits' => 'maskUnits',
|
3864 |
-
'numoctaves' => 'numOctaves',
|
3865 |
-
'pathlength' => 'pathLength',
|
3866 |
-
'patterncontentunits' => 'patternContentUnits',
|
3867 |
-
'patterntransform' => 'patternTransform',
|
3868 |
-
'patternunits' => 'patternUnits',
|
3869 |
-
'pointsatx' => 'pointsAtX',
|
3870 |
-
'pointsaty' => 'pointsAtY',
|
3871 |
-
'pointsatz' => 'pointsAtZ',
|
3872 |
-
'preservealpha' => 'preserveAlpha',
|
3873 |
-
'preserveaspectratio' => 'preserveAspectRatio',
|
3874 |
-
'primitiveunits' => 'primitiveUnits',
|
3875 |
-
'refx' => 'refX',
|
3876 |
-
'refy' => 'refY',
|
3877 |
-
'repeatcount' => 'repeatCount',
|
3878 |
-
'repeatdur' => 'repeatDur',
|
3879 |
-
'requiredextensions' => 'requiredExtensions',
|
3880 |
-
'requiredfeatures' => 'requiredFeatures',
|
3881 |
-
'specularconstant' => 'specularConstant',
|
3882 |
-
'specularexponent' => 'specularExponent',
|
3883 |
-
'spreadmethod' => 'spreadMethod',
|
3884 |
-
'startoffset' => 'startOffset',
|
3885 |
-
'stddeviation' => 'stdDeviation',
|
3886 |
-
'stitchtiles' => 'stitchTiles',
|
3887 |
-
'surfacescale' => 'surfaceScale',
|
3888 |
-
'systemlanguage' => 'systemLanguage',
|
3889 |
-
'tablevalues' => 'tableValues',
|
3890 |
-
'targetx' => 'targetX',
|
3891 |
-
'targety' => 'targetY',
|
3892 |
-
'textlength' => 'textLength',
|
3893 |
-
'viewbox' => 'viewBox',
|
3894 |
-
'viewtarget' => 'viewTarget',
|
3895 |
-
'xchannelselector' => 'xChannelSelector',
|
3896 |
-
'ychannelselector' => 'yChannelSelector',
|
3897 |
-
'zoomandpan' => 'zoomAndPan',
|
3898 |
-
];
|
3899 |
-
foreach ($token['attr'] as &$kp) {
|
3900 |
-
if (isset($lookup[$kp['name']])) {
|
3901 |
-
$kp['name'] = $lookup[$kp['name']];
|
3902 |
-
}
|
3903 |
-
}
|
3904 |
-
return $token;
|
3905 |
-
}
|
3906 |
-
|
3907 |
-
/**
|
3908 |
-
* @param $token
|
3909 |
-
* @return mixed
|
3910 |
-
*/
|
3911 |
-
public function adjustForeignAttributes($token) {
|
3912 |
-
static $lookup = [
|
3913 |
-
'xlink:actuate' => ['xlink', 'actuate', self::NS_XLINK],
|
3914 |
-
'xlink:arcrole' => ['xlink', 'arcrole', self::NS_XLINK],
|
3915 |
-
'xlink:href' => ['xlink', 'href', self::NS_XLINK],
|
3916 |
-
'xlink:role' => ['xlink', 'role', self::NS_XLINK],
|
3917 |
-
'xlink:show' => ['xlink', 'show', self::NS_XLINK],
|
3918 |
-
'xlink:title' => ['xlink', 'title', self::NS_XLINK],
|
3919 |
-
'xlink:type' => ['xlink', 'type', self::NS_XLINK],
|
3920 |
-
'xml:base' => ['xml', 'base', self::NS_XML],
|
3921 |
-
'xml:lang' => ['xml', 'lang', self::NS_XML],
|
3922 |
-
'xml:space' => ['xml', 'space', self::NS_XML],
|
3923 |
-
'xmlns' => [null, 'xmlns', self::NS_XMLNS],
|
3924 |
-
'xmlns:xlink' => ['xmlns', 'xlink', self::NS_XMLNS],
|
3925 |
-
];
|
3926 |
-
foreach ($token['attr'] as &$kp) {
|
3927 |
-
if (isset($lookup[$kp['name']])) {
|
3928 |
-
$kp['name'] = $lookup[$kp['name']];
|
3929 |
-
}
|
3930 |
-
}
|
3931 |
-
return $token;
|
3932 |
-
}
|
3933 |
-
|
3934 |
-
/**
|
3935 |
-
* @param $token
|
3936 |
-
* @param $namespaceURI
|
3937 |
-
*/
|
3938 |
-
public function insertForeignElement($token, $namespaceURI) {
|
3939 |
-
$el = $this->dom->createElementNS($namespaceURI, $token['name']);
|
3940 |
-
|
3941 |
-
if (!empty($token['attr'])) {
|
3942 |
-
foreach ($token['attr'] as $kp) {
|
3943 |
-
$attr = $kp['name'];
|
3944 |
-
if (is_array($attr)) {
|
3945 |
-
$ns = $attr[2];
|
3946 |
-
$attr = $attr[1];
|
3947 |
-
} else {
|
3948 |
-
$ns = self::NS_HTML;
|
3949 |
-
}
|
3950 |
-
if (!$el->hasAttributeNS($ns, $attr)) {
|
3951 |
-
// XSKETCHY: work around godawful libxml bug
|
3952 |
-
if ($ns === self::NS_XLINK) {
|
3953 |
-
$el->setAttribute('xlink:'.$attr, $kp['value']);
|
3954 |
-
} elseif ($ns === self::NS_HTML) {
|
3955 |
-
// Another godawful libxml bug
|
3956 |
-
$el->setAttribute($attr, $kp['value']);
|
3957 |
-
} else {
|
3958 |
-
$el->setAttributeNS($ns, $attr, $kp['value']);
|
3959 |
-
}
|
3960 |
-
}
|
3961 |
-
}
|
3962 |
-
}
|
3963 |
-
$this->appendToRealParent($el);
|
3964 |
-
$this->stack[] = $el;
|
3965 |
-
// XERROR: see below
|
3966 |
-
/* If the newly created element has an xmlns attribute in the XMLNS
|
3967 |
-
* namespace whose value is not exactly the same as the element's
|
3968 |
-
* namespace, that is a parse error. Similarly, if the newly created
|
3969 |
-
* element has an xmlns:xlink attribute in the XMLNS namespace whose
|
3970 |
-
* value is not the XLink Namespace, that is a parse error. */
|
3971 |
-
}
|
3972 |
-
|
3973 |
-
/**
|
3974 |
-
* @return DOMDocument|DOMNodeList
|
3975 |
-
*/
|
3976 |
-
public function save() {
|
3977 |
-
$this->dom->normalize();
|
3978 |
-
if (!$this->fragment) {
|
3979 |
-
return $this->dom;
|
3980 |
-
} else {
|
3981 |
-
if ($this->root) {
|
3982 |
-
return $this->root->childNodes;
|
3983 |
-
} else {
|
3984 |
-
return $this->dom->childNodes;
|
3985 |
-
}
|
3986 |
-
}
|
3987 |
-
}
|
3988 |
-
}
|
3989 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/lib/html5lib/named-character-references.ser
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
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;}}}}}}
|
|
vendor/dompdf/dompdf/lib/res/html.css
CHANGED
@@ -10,7 +10,7 @@
|
|
10 |
*
|
11 |
* Portions from Mozilla
|
12 |
* @link https://dxr.mozilla.org/mozilla-central/source/layout/style/res/html.css
|
13 |
-
* @license http://mozilla.org/MPL/2.0/ Mozilla Public License, v. 2.0
|
14 |
*
|
15 |
* Portions from W3C
|
16 |
* @link https://drafts.csswg.org/css-ui-3/#default-style-sheet
|
10 |
*
|
11 |
* Portions from Mozilla
|
12 |
* @link https://dxr.mozilla.org/mozilla-central/source/layout/style/res/html.css
|
13 |
+
* @license http://mozilla.org/MPL/2.0/ Mozilla Public License, v. 2.0
|
14 |
*
|
15 |
* Portions from W3C
|
16 |
* @link https://drafts.csswg.org/css-ui-3/#default-style-sheet
|
vendor/dompdf/dompdf/src/Adapter/CPDF.php
CHANGED
@@ -14,10 +14,10 @@ namespace Dompdf\Adapter;
|
|
14 |
|
15 |
use Dompdf\Canvas;
|
16 |
use Dompdf\Dompdf;
|
17 |
-
use Dompdf\Helpers;
|
18 |
use Dompdf\Exception;
|
|
|
|
|
19 |
use Dompdf\Image\Cache;
|
20 |
-
use Dompdf\PhpEvaluator;
|
21 |
use FontLib\Exception\FontNotFoundException;
|
22 |
|
23 |
/**
|
@@ -41,66 +41,66 @@ class CPDF implements Canvas
|
|
41 |
/**
|
42 |
* Dimensions of paper sizes in points
|
43 |
*
|
44 |
-
* @var array
|
45 |
*/
|
46 |
static $PAPER_SIZES = [
|
47 |
-
"4a0" => [0, 0, 4767.87, 6740.79],
|
48 |
-
"2a0" => [0, 0, 3370.39, 4767.87],
|
49 |
-
"a0" => [0, 0, 2383.94, 3370.39],
|
50 |
-
"a1" => [0, 0, 1683.78, 2383.94],
|
51 |
-
"a2" => [0, 0, 1190.55, 1683.78],
|
52 |
-
"a3" => [0, 0, 841.89, 1190.55],
|
53 |
-
"a4" => [0, 0, 595.28, 841.89],
|
54 |
-
"a5" => [0, 0, 419.53, 595.28],
|
55 |
-
"a6" => [0, 0, 297.64, 419.53],
|
56 |
-
"a7" => [0, 0, 209.76, 297.64],
|
57 |
-
"a8" => [0, 0, 147.40, 209.76],
|
58 |
-
"a9" => [0, 0, 104.88, 147.40],
|
59 |
-
"a10" => [0, 0, 73.70, 104.88],
|
60 |
-
"b0" => [0, 0, 2834.65, 4008.19],
|
61 |
-
"b1" => [0, 0, 2004.09, 2834.65],
|
62 |
-
"b2" => [0, 0, 1417.32, 2004.09],
|
63 |
-
"b3" => [0, 0, 1000.63, 1417.32],
|
64 |
-
"b4" => [0, 0, 708.66, 1000.63],
|
65 |
-
"b5" => [0, 0, 498.90, 708.66],
|
66 |
-
"b6" => [0, 0, 354.33, 498.90],
|
67 |
-
"b7" => [0, 0, 249.45, 354.33],
|
68 |
-
"b8" => [0, 0, 175.75, 249.45],
|
69 |
-
"b9" => [0, 0, 124.72, 175.75],
|
70 |
-
"b10" => [0, 0, 87.87, 124.72],
|
71 |
-
"c0" => [0, 0, 2599.37, 3676.54],
|
72 |
-
"c1" => [0, 0, 1836.85, 2599.37],
|
73 |
-
"c2" => [0, 0, 1298.27, 1836.85],
|
74 |
-
"c3" => [0, 0, 918.43, 1298.27],
|
75 |
-
"c4" => [0, 0, 649.13, 918.43],
|
76 |
-
"c5" => [0, 0, 459.21, 649.13],
|
77 |
-
"c6" => [0, 0, 323.15, 459.21],
|
78 |
-
"c7" => [0, 0, 229.61, 323.15],
|
79 |
-
"c8" => [0, 0, 161.57, 229.61],
|
80 |
-
"c9" => [0, 0, 113.39, 161.57],
|
81 |
-
"c10" => [0, 0, 79.37, 113.39],
|
82 |
-
"ra0" => [0, 0, 2437.80, 3458.27],
|
83 |
-
"ra1" => [0, 0, 1729.13, 2437.80],
|
84 |
-
"ra2" => [0, 0, 1218.90, 1729.13],
|
85 |
-
"ra3" => [0, 0, 864.57, 1218.90],
|
86 |
-
"ra4" => [0, 0, 609.45, 864.57],
|
87 |
-
"sra0" => [0, 0, 2551.18, 3628.35],
|
88 |
-
"sra1" => [0, 0, 1814.17, 2551.18],
|
89 |
-
"sra2" => [0, 0, 1275.59, 1814.17],
|
90 |
-
"sra3" => [0, 0, 907.09, 1275.59],
|
91 |
-
"sra4" => [0, 0, 637.80, 907.09],
|
92 |
-
"letter" => [0, 0, 612.00, 792.00],
|
93 |
-
"half-letter" => [0, 0, 396.00, 612.00],
|
94 |
-
"legal" => [0, 0, 612.00, 1008.00],
|
95 |
-
"ledger" => [0, 0, 1224.00, 792.00],
|
96 |
-
"tabloid" => [0, 0, 792.00, 1224.00],
|
97 |
-
"executive" => [0, 0, 521.86, 756.00],
|
98 |
-
"folio" => [0, 0, 612.00, 936.00],
|
99 |
-
"commercial #10 envelope" => [0, 0, 684, 297],
|
100 |
-
"catalog #10 1/2 envelope" => [0, 0, 648, 864],
|
101 |
-
"8.5x11" => [0, 0, 612.00, 792.00],
|
102 |
-
"8.5x14" => [0, 0, 612.00, 1008.
|
103 |
-
"11x17" => [0, 0, 792.00, 1224.00],
|
104 |
];
|
105 |
|
106 |
/**
|
@@ -145,13 +145,6 @@ class CPDF implements Canvas
|
|
145 |
*/
|
146 |
protected $_page_count;
|
147 |
|
148 |
-
/**
|
149 |
-
* Text to display on every page
|
150 |
-
*
|
151 |
-
* @var array
|
152 |
-
*/
|
153 |
-
protected $_page_text;
|
154 |
-
|
155 |
/**
|
156 |
* Array of pages for accessing after rendering is initially complete
|
157 |
*
|
@@ -166,24 +159,16 @@ class CPDF implements Canvas
|
|
166 |
*/
|
167 |
protected $_current_opacity = 1;
|
168 |
|
169 |
-
|
170 |
-
* Class constructor
|
171 |
-
*
|
172 |
-
* @param mixed $paper The size of paper to use in this PDF ({@link CPDF::$PAPER_SIZES})
|
173 |
-
* @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
|
174 |
-
* @param Dompdf $dompdf The Dompdf instance
|
175 |
-
*/
|
176 |
-
public function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf = null)
|
177 |
{
|
178 |
if (is_array($paper)) {
|
179 |
-
$size = $paper;
|
180 |
-
} else if (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) {
|
181 |
-
$size = self::$PAPER_SIZES[mb_strtolower($paper)];
|
182 |
} else {
|
183 |
-
$
|
|
|
184 |
}
|
185 |
|
186 |
-
if (
|
187 |
[$size[2], $size[3]] = [$size[3], $size[2]];
|
188 |
}
|
189 |
|
@@ -209,14 +194,10 @@ class CPDF implements Canvas
|
|
209 |
$this->_height = $size[3] - $size[1];
|
210 |
|
211 |
$this->_page_number = $this->_page_count = 1;
|
212 |
-
$this->_page_text = [];
|
213 |
|
214 |
$this->_pages = [$this->_pdf->getFirstPageId()];
|
215 |
}
|
216 |
|
217 |
-
/**
|
218 |
-
* @return Dompdf
|
219 |
-
*/
|
220 |
public function get_dompdf()
|
221 |
{
|
222 |
return $this->_dompdf;
|
@@ -232,13 +213,7 @@ class CPDF implements Canvas
|
|
232 |
return $this->_pdf;
|
233 |
}
|
234 |
|
235 |
-
|
236 |
-
* Add meta information to the PDF
|
237 |
-
*
|
238 |
-
* @param string $label label of the value (Creator, Producer, etc.)
|
239 |
-
* @param string $value the text to set
|
240 |
-
*/
|
241 |
-
public function add_info($label, $value)
|
242 |
{
|
243 |
$this->_pdf->addInfo($label, $value);
|
244 |
}
|
@@ -342,37 +317,21 @@ class CPDF implements Canvas
|
|
342 |
|
343 |
//........................................................................
|
344 |
|
345 |
-
/**
|
346 |
-
* Returns the PDF's width in points
|
347 |
-
* @return float
|
348 |
-
*/
|
349 |
public function get_width()
|
350 |
{
|
351 |
return $this->_width;
|
352 |
}
|
353 |
|
354 |
-
/**
|
355 |
-
* Returns the PDF's height in points
|
356 |
-
* @return float
|
357 |
-
*/
|
358 |
public function get_height()
|
359 |
{
|
360 |
return $this->_height;
|
361 |
}
|
362 |
|
363 |
-
/**
|
364 |
-
* Returns the current page number
|
365 |
-
* @return int
|
366 |
-
*/
|
367 |
public function get_page_number()
|
368 |
{
|
369 |
return $this->_page_number;
|
370 |
}
|
371 |
|
372 |
-
/**
|
373 |
-
* Returns the total number of pages in the document
|
374 |
-
* @return int
|
375 |
-
*/
|
376 |
public function get_page_count()
|
377 |
{
|
378 |
return $this->_page_count;
|
@@ -388,11 +347,6 @@ class CPDF implements Canvas
|
|
388 |
$this->_page_number = $num;
|
389 |
}
|
390 |
|
391 |
-
/**
|
392 |
-
* Sets the page count
|
393 |
-
*
|
394 |
-
* @param int $count
|
395 |
-
*/
|
396 |
public function set_page_count($count)
|
397 |
{
|
398 |
$this->_page_count = $count;
|
@@ -402,15 +356,14 @@ class CPDF implements Canvas
|
|
402 |
* Sets the stroke color
|
403 |
*
|
404 |
* See {@link Style::set_color()} for the format of the color array.
|
|
|
405 |
* @param array $color
|
406 |
*/
|
407 |
protected function _set_stroke_color($color)
|
408 |
{
|
409 |
$this->_pdf->setStrokeColor($color);
|
410 |
$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
|
411 |
-
|
412 |
-
$alpha *= $this->_current_opacity;
|
413 |
-
}
|
414 |
$this->_set_line_transparency("Normal", $alpha);
|
415 |
}
|
416 |
|
@@ -418,15 +371,14 @@ class CPDF implements Canvas
|
|
418 |
* Sets the fill colour
|
419 |
*
|
420 |
* See {@link Style::set_color()} for the format of the colour array.
|
|
|
421 |
* @param array $color
|
422 |
*/
|
423 |
protected function _set_fill_color($color)
|
424 |
{
|
425 |
$this->_pdf->setColor($color);
|
426 |
$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
|
427 |
-
|
428 |
-
$alpha *= $this->_current_opacity;
|
429 |
-
}
|
430 |
$this->_set_fill_transparency("Normal", $alpha);
|
431 |
}
|
432 |
|
@@ -440,8 +392,8 @@ class CPDF implements Canvas
|
|
440 |
* ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
|
441 |
* Exclusion
|
442 |
*
|
443 |
-
* @param string $mode
|
444 |
-
* @param float
|
445 |
*/
|
446 |
protected function _set_line_transparency($mode, $opacity)
|
447 |
{
|
@@ -458,8 +410,8 @@ class CPDF implements Canvas
|
|
458 |
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
459 |
* Exclusion
|
460 |
*
|
461 |
-
* @param string $mode
|
462 |
-
* @param float
|
463 |
*/
|
464 |
protected function _set_fill_transparency($mode, $opacity)
|
465 |
{
|
@@ -471,23 +423,17 @@ class CPDF implements Canvas
|
|
471 |
*
|
472 |
* @see Cpdf::setLineStyle()
|
473 |
*
|
474 |
-
* @param float
|
475 |
* @param string $cap
|
476 |
* @param string $join
|
477 |
-
* @param array
|
478 |
*/
|
479 |
protected function _set_line_style($width, $cap, $join, $dash)
|
480 |
{
|
481 |
$this->_pdf->setLineStyle($width, $cap, $join, $dash);
|
482 |
}
|
483 |
|
484 |
-
|
485 |
-
* Sets the opacity
|
486 |
-
*
|
487 |
-
* @param $opacity
|
488 |
-
* @param $mode
|
489 |
-
*/
|
490 |
-
public function set_opacity($opacity, $mode = "Normal")
|
491 |
{
|
492 |
$this->_set_line_transparency($mode, $opacity);
|
493 |
$this->_set_fill_transparency($mode, $opacity);
|
@@ -511,90 +457,33 @@ class CPDF implements Canvas
|
|
511 |
return $this->_height - $y;
|
512 |
}
|
513 |
|
514 |
-
|
515 |
-
* Canvas implementation
|
516 |
-
*
|
517 |
-
* @param float $x1
|
518 |
-
* @param float $y1
|
519 |
-
* @param float $x2
|
520 |
-
* @param float $y2
|
521 |
-
* @param array $color
|
522 |
-
* @param float $width
|
523 |
-
* @param array $style
|
524 |
-
*/
|
525 |
-
public function line($x1, $y1, $x2, $y2, $color, $width, $style = [])
|
526 |
{
|
527 |
$this->_set_stroke_color($color);
|
528 |
-
$this->_set_line_style($width,
|
529 |
|
530 |
$this->_pdf->line($x1, $this->y($y1),
|
531 |
$x2, $this->y($y2));
|
532 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
533 |
}
|
534 |
|
535 |
-
|
536 |
-
* Draw line at the specified coordinates on every page.
|
537 |
-
*
|
538 |
-
* See {@link Style::munge_color()} for the format of the colour array.
|
539 |
-
*
|
540 |
-
* @param float $x1
|
541 |
-
* @param float $y1
|
542 |
-
* @param float $x2
|
543 |
-
* @param float $y2
|
544 |
-
* @param array $color
|
545 |
-
* @param float $width
|
546 |
-
* @param array $style optional
|
547 |
-
*/
|
548 |
-
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
|
549 |
-
{
|
550 |
-
$_t = 'line';
|
551 |
-
$this->_page_text[] = compact('_t', 'x1', 'y1', 'x2', 'y2', 'color', 'width', 'style');
|
552 |
-
}
|
553 |
-
|
554 |
-
/**
|
555 |
-
* @param float $x
|
556 |
-
* @param float $y
|
557 |
-
* @param float $r1
|
558 |
-
* @param float $r2
|
559 |
-
* @param float $astart
|
560 |
-
* @param float $aend
|
561 |
-
* @param array $color
|
562 |
-
* @param float $width
|
563 |
-
* @param array $style
|
564 |
-
*/
|
565 |
-
public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [])
|
566 |
{
|
567 |
$this->_set_stroke_color($color);
|
568 |
-
$this->_set_line_style($width,
|
569 |
|
570 |
$this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
|
571 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
572 |
}
|
573 |
|
574 |
-
|
575 |
-
* @param float $x1
|
576 |
-
* @param float $y1
|
577 |
-
* @param float $w
|
578 |
-
* @param float $h
|
579 |
-
* @param array $color
|
580 |
-
* @param float $width
|
581 |
-
* @param array $style
|
582 |
-
*/
|
583 |
-
public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [])
|
584 |
{
|
585 |
$this->_set_stroke_color($color);
|
586 |
-
$this->_set_line_style($width,
|
587 |
$this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
|
588 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
589 |
}
|
590 |
|
591 |
-
/**
|
592 |
-
* @param float $x1
|
593 |
-
* @param float $y1
|
594 |
-
* @param float $w
|
595 |
-
* @param float $h
|
596 |
-
* @param array $color
|
597 |
-
*/
|
598 |
public function filled_rectangle($x1, $y1, $w, $h, $color)
|
599 |
{
|
600 |
$this->_set_fill_color($color);
|
@@ -602,143 +491,87 @@ class CPDF implements Canvas
|
|
602 |
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
603 |
}
|
604 |
|
605 |
-
/**
|
606 |
-
* @param float $x1
|
607 |
-
* @param float $y1
|
608 |
-
* @param float $w
|
609 |
-
* @param float $h
|
610 |
-
*/
|
611 |
public function clipping_rectangle($x1, $y1, $w, $h)
|
612 |
{
|
613 |
$this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
|
614 |
}
|
615 |
|
616 |
-
/**
|
617 |
-
* @param float $x1
|
618 |
-
* @param float $y1
|
619 |
-
* @param float $w
|
620 |
-
* @param float $h
|
621 |
-
* @param float $rTL
|
622 |
-
* @param float $rTR
|
623 |
-
* @param float $rBR
|
624 |
-
* @param float $rBL
|
625 |
-
*/
|
626 |
public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
627 |
{
|
628 |
$this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
|
629 |
}
|
630 |
|
631 |
-
|
632 |
-
|
633 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
634 |
public function clipping_end()
|
635 |
{
|
636 |
$this->_pdf->clippingEnd();
|
637 |
}
|
638 |
|
639 |
-
/**
|
640 |
-
*
|
641 |
-
*/
|
642 |
public function save()
|
643 |
{
|
644 |
$this->_pdf->saveState();
|
645 |
}
|
646 |
|
647 |
-
/**
|
648 |
-
*
|
649 |
-
*/
|
650 |
public function restore()
|
651 |
{
|
652 |
$this->_pdf->restoreState();
|
653 |
}
|
654 |
|
655 |
-
/**
|
656 |
-
* @param $angle
|
657 |
-
* @param $x
|
658 |
-
* @param $y
|
659 |
-
*/
|
660 |
public function rotate($angle, $x, $y)
|
661 |
{
|
662 |
$this->_pdf->rotate($angle, $x, $y);
|
663 |
}
|
664 |
|
665 |
-
/**
|
666 |
-
* @param $angle_x
|
667 |
-
* @param $angle_y
|
668 |
-
* @param $x
|
669 |
-
* @param $y
|
670 |
-
*/
|
671 |
public function skew($angle_x, $angle_y, $x, $y)
|
672 |
{
|
673 |
$this->_pdf->skew($angle_x, $angle_y, $x, $y);
|
674 |
}
|
675 |
|
676 |
-
/**
|
677 |
-
* @param $s_x
|
678 |
-
* @param $s_y
|
679 |
-
* @param $x
|
680 |
-
* @param $y
|
681 |
-
*/
|
682 |
public function scale($s_x, $s_y, $x, $y)
|
683 |
{
|
684 |
$this->_pdf->scale($s_x, $s_y, $x, $y);
|
685 |
}
|
686 |
|
687 |
-
/**
|
688 |
-
* @param $t_x
|
689 |
-
* @param $t_y
|
690 |
-
*/
|
691 |
public function translate($t_x, $t_y)
|
692 |
{
|
693 |
$this->_pdf->translate($t_x, $t_y);
|
694 |
}
|
695 |
|
696 |
-
/**
|
697 |
-
* @param $a
|
698 |
-
* @param $b
|
699 |
-
* @param $c
|
700 |
-
* @param $d
|
701 |
-
* @param $e
|
702 |
-
* @param $f
|
703 |
-
*/
|
704 |
public function transform($a, $b, $c, $d, $e, $f)
|
705 |
{
|
706 |
$this->_pdf->transform([$a, $b, $c, $d, $e, $f]);
|
707 |
}
|
708 |
|
709 |
-
/**
|
710 |
-
* @param array $points
|
711 |
-
* @param array $color
|
712 |
-
* @param null $width
|
713 |
-
* @param array $style
|
714 |
-
* @param bool $fill
|
715 |
-
*/
|
716 |
public function polygon($points, $color, $width = null, $style = [], $fill = false)
|
717 |
{
|
718 |
$this->_set_fill_color($color);
|
719 |
$this->_set_stroke_color($color);
|
720 |
|
|
|
|
|
|
|
|
|
721 |
// Adjust y values
|
722 |
for ($i = 1; $i < count($points); $i += 2) {
|
723 |
$points[$i] = $this->y($points[$i]);
|
724 |
}
|
725 |
|
726 |
-
$this->_pdf->polygon($points,
|
727 |
|
728 |
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
729 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
730 |
}
|
731 |
|
732 |
-
|
733 |
-
* @param float $x
|
734 |
-
* @param float $y
|
735 |
-
* @param float $r1
|
736 |
-
* @param array $color
|
737 |
-
* @param null $width
|
738 |
-
* @param null $style
|
739 |
-
* @param bool $fill
|
740 |
-
*/
|
741 |
-
public function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false)
|
742 |
{
|
743 |
$this->_set_fill_color($color);
|
744 |
$this->_set_stroke_color($color);
|
@@ -747,7 +580,7 @@ class CPDF implements Canvas
|
|
747 |
$this->_set_line_style($width, "round", "round", $style);
|
748 |
}
|
749 |
|
750 |
-
$this->_pdf->ellipse($x, $this->y($y), $
|
751 |
|
752 |
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
753 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
@@ -759,8 +592,7 @@ class CPDF implements Canvas
|
|
759 |
* @param string $image_url
|
760 |
* @param string $type
|
761 |
*
|
762 |
-
* @
|
763 |
-
* @return string The url of the newly converted image
|
764 |
*/
|
765 |
protected function _convert_to_png($image_url, $type)
|
766 |
{
|
@@ -772,6 +604,8 @@ class CPDF implements Canvas
|
|
772 |
|
773 |
$func_name = "imagecreatefrom$type";
|
774 |
|
|
|
|
|
775 |
if (!function_exists($func_name)) {
|
776 |
if (!method_exists(Helpers::class, $func_name)) {
|
777 |
throw new Exception("Function $func_name() not found. Cannot convert $type image: $image_url. Please install the image PHP extension.");
|
@@ -779,8 +613,6 @@ class CPDF implements Canvas
|
|
779 |
$func_name = [Helpers::class, $func_name];
|
780 |
}
|
781 |
|
782 |
-
set_error_handler([Helpers::class, "record_warnings"]);
|
783 |
-
|
784 |
try {
|
785 |
$im = call_user_func($func_name, $image_url);
|
786 |
|
@@ -795,25 +627,19 @@ class CPDF implements Canvas
|
|
795 |
imagepng($im, $filename);
|
796 |
imagedestroy($im);
|
797 |
} else {
|
798 |
-
$filename =
|
799 |
}
|
800 |
} finally {
|
801 |
restore_error_handler();
|
802 |
}
|
803 |
|
804 |
-
|
|
|
|
|
805 |
|
806 |
return $filename;
|
807 |
}
|
808 |
|
809 |
-
/**
|
810 |
-
* @param string $img
|
811 |
-
* @param float $x
|
812 |
-
* @param float $y
|
813 |
-
* @param int $w
|
814 |
-
* @param int $h
|
815 |
-
* @param string $resolution
|
816 |
-
*/
|
817 |
public function image($img, $x, $y, $w, $h, $resolution = "normal")
|
818 |
{
|
819 |
[$width, $height, $type] = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
|
@@ -839,6 +665,11 @@ class CPDF implements Canvas
|
|
839 |
case "bmp":
|
840 |
if ($debug_png) print "!!!{$type}!!!";
|
841 |
$img = $this->_convert_to_png($img, $type);
|
|
|
|
|
|
|
|
|
|
|
842 |
|
843 |
case "png":
|
844 |
if ($debug_png) print '!!!png!!!';
|
@@ -922,17 +753,6 @@ class CPDF implements Canvas
|
|
922 |
$pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
|
923 |
}
|
924 |
|
925 |
-
/**
|
926 |
-
* @param float $x
|
927 |
-
* @param float $y
|
928 |
-
* @param string $text
|
929 |
-
* @param string $font
|
930 |
-
* @param float $size
|
931 |
-
* @param array $color
|
932 |
-
* @param float $word_space
|
933 |
-
* @param float $char_space
|
934 |
-
* @param float $angle
|
935 |
-
*/
|
936 |
public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
|
937 |
{
|
938 |
$pdf = $this->_pdf;
|
@@ -947,9 +767,6 @@ class CPDF implements Canvas
|
|
947 |
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
948 |
}
|
949 |
|
950 |
-
/**
|
951 |
-
* @param string $code
|
952 |
-
*/
|
953 |
public function javascript($code)
|
954 |
{
|
955 |
$this->_pdf->addJavascript($code);
|
@@ -957,25 +774,11 @@ class CPDF implements Canvas
|
|
957 |
|
958 |
//........................................................................
|
959 |
|
960 |
-
/**
|
961 |
-
* Add a named destination (similar to <a name="foo">...</a> in html)
|
962 |
-
*
|
963 |
-
* @param string $anchorname The name of the named destination
|
964 |
-
*/
|
965 |
public function add_named_dest($anchorname)
|
966 |
{
|
967 |
$this->_pdf->addDestination($anchorname, "Fit");
|
968 |
}
|
969 |
|
970 |
-
/**
|
971 |
-
* Add a link to the pdf
|
972 |
-
*
|
973 |
-
* @param string $url The url to link to
|
974 |
-
* @param float $x The x position of the link
|
975 |
-
* @param float $y The y position of the link
|
976 |
-
* @param float $width The width of the link
|
977 |
-
* @param float $height The height of the link
|
978 |
-
*/
|
979 |
public function add_link($url, $x, $y, $width, $height)
|
980 |
{
|
981 |
$y = $this->y($y) - $height;
|
@@ -987,28 +790,20 @@ class CPDF implements Canvas
|
|
987 |
$this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
|
988 |
}
|
989 |
} else {
|
990 |
-
$this->_pdf->addLink(
|
991 |
}
|
992 |
}
|
993 |
|
994 |
/**
|
995 |
-
* @
|
996 |
-
* @param string $font
|
997 |
-
* @param float $size
|
998 |
-
* @param float $word_spacing
|
999 |
-
* @param float $char_spacing
|
1000 |
-
* @return float
|
1001 |
*/
|
1002 |
-
public function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0)
|
1003 |
{
|
1004 |
$this->_pdf->selectFont($font, '', true, $this->_dompdf->getOptions()->getIsFontSubsettingEnabled());
|
1005 |
return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
|
1006 |
}
|
1007 |
|
1008 |
/**
|
1009 |
-
* @param string $font
|
1010 |
-
* @param float $size
|
1011 |
-
* @return float|int
|
1012 |
* @throws FontNotFoundException
|
1013 |
*/
|
1014 |
public function get_font_height($font, $size)
|
@@ -1026,9 +821,7 @@ class CPDF implements Canvas
|
|
1026 |
}*/
|
1027 |
|
1028 |
/**
|
1029 |
-
* @
|
1030 |
-
* @param float $size
|
1031 |
-
* @return float
|
1032 |
*/
|
1033 |
public function get_font_baseline($font, $size)
|
1034 |
{
|
@@ -1037,53 +830,53 @@ class CPDF implements Canvas
|
|
1037 |
}
|
1038 |
|
1039 |
/**
|
1040 |
-
*
|
1041 |
*
|
1042 |
-
* The
|
1043 |
-
*
|
|
|
|
|
|
|
1044 |
*
|
1045 |
-
*
|
|
|
1046 |
*
|
1047 |
-
* @param
|
1048 |
-
* @param float $y
|
1049 |
-
* @param string $text the text to write
|
1050 |
-
* @param string $font the font file to use
|
1051 |
-
* @param float $size the font size, in points
|
1052 |
-
* @param array $color
|
1053 |
-
* @param float $word_space word spacing adjustment
|
1054 |
-
* @param float $char_space char spacing adjustment
|
1055 |
-
* @param float $angle angle to write the text at, measured CW starting from the x-axis
|
1056 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1057 |
public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
|
1058 |
{
|
1059 |
-
$
|
1060 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1061 |
}
|
1062 |
|
1063 |
-
|
1064 |
-
* Processes a callback or script on every page
|
1065 |
-
*
|
1066 |
-
* The callback function receives the four parameters `$pageNumber`,
|
1067 |
-
* `$pageCount`, `$pdf`, and `$fontMetrics`, in that order. If a script is
|
1068 |
-
* passed as string, the variables `$PAGE_NUM`, `$PAGE_COUNT`, `$pdf`, and
|
1069 |
-
* `$fontMetrics` are available instead.
|
1070 |
-
*
|
1071 |
-
* This function can be used to add page numbers to all pages after the
|
1072 |
-
* first one, for example.
|
1073 |
-
*
|
1074 |
-
* @param callable|string $code The callback function or PHP script to process on every page
|
1075 |
-
*/
|
1076 |
-
public function page_script($code)
|
1077 |
{
|
1078 |
-
|
1079 |
-
$this->
|
1080 |
-
|
1081 |
-
"callback" => $code
|
1082 |
-
];
|
1083 |
-
} else {
|
1084 |
-
$_t = "script";
|
1085 |
-
$this->_page_text[] = compact("_t", "code");
|
1086 |
-
}
|
1087 |
}
|
1088 |
|
1089 |
/**
|
@@ -1099,60 +892,21 @@ class CPDF implements Canvas
|
|
1099 |
return $ret;
|
1100 |
}
|
1101 |
|
1102 |
-
|
1103 |
-
* Add text to each page after rendering is complete
|
1104 |
-
*/
|
1105 |
-
protected function _add_page_text()
|
1106 |
{
|
1107 |
-
|
1108 |
-
return;
|
1109 |
-
}
|
1110 |
-
|
1111 |
-
$page_number = 1;
|
1112 |
-
$eval = null;
|
1113 |
|
1114 |
foreach ($this->_pages as $pid) {
|
1115 |
$this->reopen_object($pid);
|
1116 |
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
switch ($_t) {
|
1121 |
-
case "text":
|
1122 |
-
$text = str_replace(["{PAGE_NUM}", "{PAGE_COUNT}"],
|
1123 |
-
[$page_number, $this->_page_count], $text);
|
1124 |
-
$this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
|
1125 |
-
break;
|
1126 |
-
|
1127 |
-
case "callback":
|
1128 |
-
$fontMetrics = $this->get_dompdf()->getFontMetrics();
|
1129 |
-
$callback($page_number, $this->_page_count, $this, $fontMetrics);
|
1130 |
-
break;
|
1131 |
-
|
1132 |
-
case "script":
|
1133 |
-
if (!$eval) {
|
1134 |
-
$eval = new PhpEvaluator($this);
|
1135 |
-
}
|
1136 |
-
$eval->evaluate($code, ["PAGE_NUM" => $page_number, "PAGE_COUNT" => $this->_page_count]);
|
1137 |
-
break;
|
1138 |
-
|
1139 |
-
case "line":
|
1140 |
-
$this->line($x1, $y1, $x2, $y2, $color, $width, $style);
|
1141 |
-
break;
|
1142 |
-
}
|
1143 |
-
}
|
1144 |
|
1145 |
$this->close_object();
|
1146 |
-
$
|
1147 |
}
|
1148 |
}
|
1149 |
|
1150 |
-
/**
|
1151 |
-
* Streams the PDF to the client.
|
1152 |
-
*
|
1153 |
-
* @param string $filename The filename to present to the client.
|
1154 |
-
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
|
1155 |
-
*/
|
1156 |
public function stream($filename = "document.pdf", $options = [])
|
1157 |
{
|
1158 |
if (headers_sent()) {
|
@@ -1162,8 +916,6 @@ class CPDF implements Canvas
|
|
1162 |
if (!isset($options["compress"])) $options["compress"] = true;
|
1163 |
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
1164 |
|
1165 |
-
$this->_add_page_text();
|
1166 |
-
|
1167 |
$debug = !$options['compress'];
|
1168 |
$tmp = ltrim($this->_pdf->output($debug));
|
1169 |
|
@@ -1179,18 +931,10 @@ class CPDF implements Canvas
|
|
1179 |
flush();
|
1180 |
}
|
1181 |
|
1182 |
-
/**
|
1183 |
-
* Returns the PDF as a string.
|
1184 |
-
*
|
1185 |
-
* @param array $options Associative array: 'compress' => 1 or 0 (default 1).
|
1186 |
-
* @return string
|
1187 |
-
*/
|
1188 |
public function output($options = [])
|
1189 |
{
|
1190 |
if (!isset($options["compress"])) $options["compress"] = true;
|
1191 |
|
1192 |
-
$this->_add_page_text();
|
1193 |
-
|
1194 |
$debug = !$options['compress'];
|
1195 |
|
1196 |
return $this->_pdf->output($debug);
|
14 |
|
15 |
use Dompdf\Canvas;
|
16 |
use Dompdf\Dompdf;
|
|
|
17 |
use Dompdf\Exception;
|
18 |
+
use Dompdf\FontMetrics;
|
19 |
+
use Dompdf\Helpers;
|
20 |
use Dompdf\Image\Cache;
|
|
|
21 |
use FontLib\Exception\FontNotFoundException;
|
22 |
|
23 |
/**
|
41 |
/**
|
42 |
* Dimensions of paper sizes in points
|
43 |
*
|
44 |
+
* @var array
|
45 |
*/
|
46 |
static $PAPER_SIZES = [
|
47 |
+
"4a0" => [0.0, 0.0, 4767.87, 6740.79],
|
48 |
+
"2a0" => [0.0, 0.0, 3370.39, 4767.87],
|
49 |
+
"a0" => [0.0, 0.0, 2383.94, 3370.39],
|
50 |
+
"a1" => [0.0, 0.0, 1683.78, 2383.94],
|
51 |
+
"a2" => [0.0, 0.0, 1190.55, 1683.78],
|
52 |
+
"a3" => [0.0, 0.0, 841.89, 1190.55],
|
53 |
+
"a4" => [0.0, 0.0, 595.28, 841.89],
|
54 |
+
"a5" => [0.0, 0.0, 419.53, 595.28],
|
55 |
+
"a6" => [0.0, 0.0, 297.64, 419.53],
|
56 |
+
"a7" => [0.0, 0.0, 209.76, 297.64],
|
57 |
+
"a8" => [0.0, 0.0, 147.40, 209.76],
|
58 |
+
"a9" => [0.0, 0.0, 104.88, 147.40],
|
59 |
+
"a10" => [0.0, 0.0, 73.70, 104.88],
|
60 |
+
"b0" => [0.0, 0.0, 2834.65, 4008.19],
|
61 |
+
"b1" => [0.0, 0.0, 2004.09, 2834.65],
|
62 |
+
"b2" => [0.0, 0.0, 1417.32, 2004.09],
|
63 |
+
"b3" => [0.0, 0.0, 1000.63, 1417.32],
|
64 |
+
"b4" => [0.0, 0.0, 708.66, 1000.63],
|
65 |
+
"b5" => [0.0, 0.0, 498.90, 708.66],
|
66 |
+
"b6" => [0.0, 0.0, 354.33, 498.90],
|
67 |
+
"b7" => [0.0, 0.0, 249.45, 354.33],
|
68 |
+
"b8" => [0.0, 0.0, 175.75, 249.45],
|
69 |
+
"b9" => [0.0, 0.0, 124.72, 175.75],
|
70 |
+
"b10" => [0.0, 0.0, 87.87, 124.72],
|
71 |
+
"c0" => [0.0, 0.0, 2599.37, 3676.54],
|
72 |
+
"c1" => [0.0, 0.0, 1836.85, 2599.37],
|
73 |
+
"c2" => [0.0, 0.0, 1298.27, 1836.85],
|
74 |
+
"c3" => [0.0, 0.0, 918.43, 1298.27],
|
75 |
+
"c4" => [0.0, 0.0, 649.13, 918.43],
|
76 |
+
"c5" => [0.0, 0.0, 459.21, 649.13],
|
77 |
+
"c6" => [0.0, 0.0, 323.15, 459.21],
|
78 |
+
"c7" => [0.0, 0.0, 229.61, 323.15],
|
79 |
+
"c8" => [0.0, 0.0, 161.57, 229.61],
|
80 |
+
"c9" => [0.0, 0.0, 113.39, 161.57],
|
81 |
+
"c10" => [0.0, 0.0, 79.37, 113.39],
|
82 |
+
"ra0" => [0.0, 0.0, 2437.80, 3458.27],
|
83 |
+
"ra1" => [0.0, 0.0, 1729.13, 2437.80],
|
84 |
+
"ra2" => [0.0, 0.0, 1218.90, 1729.13],
|
85 |
+
"ra3" => [0.0, 0.0, 864.57, 1218.90],
|
86 |
+
"ra4" => [0.0, 0.0, 609.45, 864.57],
|
87 |
+
"sra0" => [0.0, 0.0, 2551.18, 3628.35],
|
88 |
+
"sra1" => [0.0, 0.0, 1814.17, 2551.18],
|
89 |
+
"sra2" => [0.0, 0.0, 1275.59, 1814.17],
|
90 |
+
"sra3" => [0.0, 0.0, 907.09, 1275.59],
|
91 |
+
"sra4" => [0.0, 0.0, 637.80, 907.09],
|
92 |
+
"letter" => [0.0, 0.0, 612.00, 792.00],
|
93 |
+
"half-letter" => [0.0, 0.0, 396.00, 612.00],
|
94 |
+
"legal" => [0.0, 0.0, 612.00, 1008.00],
|
95 |
+
"ledger" => [0.0, 0.0, 1224.00, 792.00],
|
96 |
+
"tabloid" => [0.0, 0.0, 792.00, 1224.00],
|
97 |
+
"executive" => [0.0, 0.0, 521.86, 756.00],
|
98 |
+
"folio" => [0.0, 0.0, 612.00, 936.00],
|
99 |
+
"commercial #10 envelope" => [0.0, 0.0, 684.00, 297.00],
|
100 |
+
"catalog #10 1/2 envelope" => [0.0, 0.0, 648.00, 864.00],
|
101 |
+
"8.5x11" => [0.0, 0.0, 612.00, 792.00],
|
102 |
+
"8.5x14" => [0.0, 0.0, 612.00, 1008.00],
|
103 |
+
"11x17" => [0.0, 0.0, 792.00, 1224.00],
|
104 |
];
|
105 |
|
106 |
/**
|
145 |
*/
|
146 |
protected $_page_count;
|
147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
/**
|
149 |
* Array of pages for accessing after rendering is initially complete
|
150 |
*
|
159 |
*/
|
160 |
protected $_current_opacity = 1;
|
161 |
|
162 |
+
public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
{
|
164 |
if (is_array($paper)) {
|
165 |
+
$size = array_map("floatval", $paper);
|
|
|
|
|
166 |
} else {
|
167 |
+
$paper = strtolower($paper);
|
168 |
+
$size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"];
|
169 |
}
|
170 |
|
171 |
+
if (strtolower($orientation) === "landscape") {
|
172 |
[$size[2], $size[3]] = [$size[3], $size[2]];
|
173 |
}
|
174 |
|
194 |
$this->_height = $size[3] - $size[1];
|
195 |
|
196 |
$this->_page_number = $this->_page_count = 1;
|
|
|
197 |
|
198 |
$this->_pages = [$this->_pdf->getFirstPageId()];
|
199 |
}
|
200 |
|
|
|
|
|
|
|
201 |
public function get_dompdf()
|
202 |
{
|
203 |
return $this->_dompdf;
|
213 |
return $this->_pdf;
|
214 |
}
|
215 |
|
216 |
+
public function add_info(string $label, string $value): void
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
{
|
218 |
$this->_pdf->addInfo($label, $value);
|
219 |
}
|
317 |
|
318 |
//........................................................................
|
319 |
|
|
|
|
|
|
|
|
|
320 |
public function get_width()
|
321 |
{
|
322 |
return $this->_width;
|
323 |
}
|
324 |
|
|
|
|
|
|
|
|
|
325 |
public function get_height()
|
326 |
{
|
327 |
return $this->_height;
|
328 |
}
|
329 |
|
|
|
|
|
|
|
|
|
330 |
public function get_page_number()
|
331 |
{
|
332 |
return $this->_page_number;
|
333 |
}
|
334 |
|
|
|
|
|
|
|
|
|
335 |
public function get_page_count()
|
336 |
{
|
337 |
return $this->_page_count;
|
347 |
$this->_page_number = $num;
|
348 |
}
|
349 |
|
|
|
|
|
|
|
|
|
|
|
350 |
public function set_page_count($count)
|
351 |
{
|
352 |
$this->_page_count = $count;
|
356 |
* Sets the stroke color
|
357 |
*
|
358 |
* See {@link Style::set_color()} for the format of the color array.
|
359 |
+
*
|
360 |
* @param array $color
|
361 |
*/
|
362 |
protected function _set_stroke_color($color)
|
363 |
{
|
364 |
$this->_pdf->setStrokeColor($color);
|
365 |
$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
|
366 |
+
$alpha *= $this->_current_opacity;
|
|
|
|
|
367 |
$this->_set_line_transparency("Normal", $alpha);
|
368 |
}
|
369 |
|
371 |
* Sets the fill colour
|
372 |
*
|
373 |
* See {@link Style::set_color()} for the format of the colour array.
|
374 |
+
*
|
375 |
* @param array $color
|
376 |
*/
|
377 |
protected function _set_fill_color($color)
|
378 |
{
|
379 |
$this->_pdf->setColor($color);
|
380 |
$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
|
381 |
+
$alpha *= $this->_current_opacity;
|
|
|
|
|
382 |
$this->_set_fill_transparency("Normal", $alpha);
|
383 |
}
|
384 |
|
392 |
* ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
|
393 |
* Exclusion
|
394 |
*
|
395 |
+
* @param string $mode the blending mode to use
|
396 |
+
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
397 |
*/
|
398 |
protected function _set_line_transparency($mode, $opacity)
|
399 |
{
|
410 |
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
|
411 |
* Exclusion
|
412 |
*
|
413 |
+
* @param string $mode the blending mode to use
|
414 |
+
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
|
415 |
*/
|
416 |
protected function _set_fill_transparency($mode, $opacity)
|
417 |
{
|
423 |
*
|
424 |
* @see Cpdf::setLineStyle()
|
425 |
*
|
426 |
+
* @param float $width
|
427 |
* @param string $cap
|
428 |
* @param string $join
|
429 |
+
* @param array $dash
|
430 |
*/
|
431 |
protected function _set_line_style($width, $cap, $join, $dash)
|
432 |
{
|
433 |
$this->_pdf->setLineStyle($width, $cap, $join, $dash);
|
434 |
}
|
435 |
|
436 |
+
public function set_opacity(float $opacity, string $mode = "Normal"): void
|
|
|
|
|
|
|
|
|
|
|
|
|
437 |
{
|
438 |
$this->_set_line_transparency($mode, $opacity);
|
439 |
$this->_set_fill_transparency($mode, $opacity);
|
457 |
return $this->_height - $y;
|
458 |
}
|
459 |
|
460 |
+
public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
461 |
{
|
462 |
$this->_set_stroke_color($color);
|
463 |
+
$this->_set_line_style($width, $cap, "", $style);
|
464 |
|
465 |
$this->_pdf->line($x1, $this->y($y1),
|
466 |
$x2, $this->y($y2));
|
467 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
468 |
}
|
469 |
|
470 |
+
public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
471 |
{
|
472 |
$this->_set_stroke_color($color);
|
473 |
+
$this->_set_line_style($width, $cap, "", $style);
|
474 |
|
475 |
$this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
|
476 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
477 |
}
|
478 |
|
479 |
+
public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
480 |
{
|
481 |
$this->_set_stroke_color($color);
|
482 |
+
$this->_set_line_style($width, $cap, "", $style);
|
483 |
$this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
|
484 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
485 |
}
|
486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
487 |
public function filled_rectangle($x1, $y1, $w, $h, $color)
|
488 |
{
|
489 |
$this->_set_fill_color($color);
|
491 |
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
492 |
}
|
493 |
|
|
|
|
|
|
|
|
|
|
|
|
|
494 |
public function clipping_rectangle($x1, $y1, $w, $h)
|
495 |
{
|
496 |
$this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
|
497 |
}
|
498 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
499 |
public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
500 |
{
|
501 |
$this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
|
502 |
}
|
503 |
|
504 |
+
public function clipping_polygon(array $points): void
|
505 |
+
{
|
506 |
+
// Adjust y values
|
507 |
+
for ($i = 1; $i < count($points); $i += 2) {
|
508 |
+
$points[$i] = $this->y($points[$i]);
|
509 |
+
}
|
510 |
+
|
511 |
+
$this->_pdf->clippingPolygon($points);
|
512 |
+
}
|
513 |
+
|
514 |
public function clipping_end()
|
515 |
{
|
516 |
$this->_pdf->clippingEnd();
|
517 |
}
|
518 |
|
|
|
|
|
|
|
519 |
public function save()
|
520 |
{
|
521 |
$this->_pdf->saveState();
|
522 |
}
|
523 |
|
|
|
|
|
|
|
524 |
public function restore()
|
525 |
{
|
526 |
$this->_pdf->restoreState();
|
527 |
}
|
528 |
|
|
|
|
|
|
|
|
|
|
|
529 |
public function rotate($angle, $x, $y)
|
530 |
{
|
531 |
$this->_pdf->rotate($angle, $x, $y);
|
532 |
}
|
533 |
|
|
|
|
|
|
|
|
|
|
|
|
|
534 |
public function skew($angle_x, $angle_y, $x, $y)
|
535 |
{
|
536 |
$this->_pdf->skew($angle_x, $angle_y, $x, $y);
|
537 |
}
|
538 |
|
|
|
|
|
|
|
|
|
|
|
|
|
539 |
public function scale($s_x, $s_y, $x, $y)
|
540 |
{
|
541 |
$this->_pdf->scale($s_x, $s_y, $x, $y);
|
542 |
}
|
543 |
|
|
|
|
|
|
|
|
|
544 |
public function translate($t_x, $t_y)
|
545 |
{
|
546 |
$this->_pdf->translate($t_x, $t_y);
|
547 |
}
|
548 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
549 |
public function transform($a, $b, $c, $d, $e, $f)
|
550 |
{
|
551 |
$this->_pdf->transform([$a, $b, $c, $d, $e, $f]);
|
552 |
}
|
553 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
554 |
public function polygon($points, $color, $width = null, $style = [], $fill = false)
|
555 |
{
|
556 |
$this->_set_fill_color($color);
|
557 |
$this->_set_stroke_color($color);
|
558 |
|
559 |
+
if (!$fill && isset($width)) {
|
560 |
+
$this->_set_line_style($width, "square", "miter", $style);
|
561 |
+
}
|
562 |
+
|
563 |
// Adjust y values
|
564 |
for ($i = 1; $i < count($points); $i += 2) {
|
565 |
$points[$i] = $this->y($points[$i]);
|
566 |
}
|
567 |
|
568 |
+
$this->_pdf->polygon($points, $fill);
|
569 |
|
570 |
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
571 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
572 |
}
|
573 |
|
574 |
+
public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
575 |
{
|
576 |
$this->_set_fill_color($color);
|
577 |
$this->_set_stroke_color($color);
|
580 |
$this->_set_line_style($width, "round", "round", $style);
|
581 |
}
|
582 |
|
583 |
+
$this->_pdf->ellipse($x, $this->y($y), $r, 0, 0, 8, 0, 360, 1, $fill);
|
584 |
|
585 |
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
586 |
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
592 |
* @param string $image_url
|
593 |
* @param string $type
|
594 |
*
|
595 |
+
* @return string|null The url of the newly converted image
|
|
|
596 |
*/
|
597 |
protected function _convert_to_png($image_url, $type)
|
598 |
{
|
604 |
|
605 |
$func_name = "imagecreatefrom$type";
|
606 |
|
607 |
+
set_error_handler([Helpers::class, "record_warnings"]);
|
608 |
+
|
609 |
if (!function_exists($func_name)) {
|
610 |
if (!method_exists(Helpers::class, $func_name)) {
|
611 |
throw new Exception("Function $func_name() not found. Cannot convert $type image: $image_url. Please install the image PHP extension.");
|
613 |
$func_name = [Helpers::class, $func_name];
|
614 |
}
|
615 |
|
|
|
|
|
616 |
try {
|
617 |
$im = call_user_func($func_name, $image_url);
|
618 |
|
627 |
imagepng($im, $filename);
|
628 |
imagedestroy($im);
|
629 |
} else {
|
630 |
+
$filename = null;
|
631 |
}
|
632 |
} finally {
|
633 |
restore_error_handler();
|
634 |
}
|
635 |
|
636 |
+
if ($filename !== null) {
|
637 |
+
Cache::addTempImage($image_url, $filename);
|
638 |
+
}
|
639 |
|
640 |
return $filename;
|
641 |
}
|
642 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
643 |
public function image($img, $x, $y, $w, $h, $resolution = "normal")
|
644 |
{
|
645 |
[$width, $height, $type] = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
|
665 |
case "bmp":
|
666 |
if ($debug_png) print "!!!{$type}!!!";
|
667 |
$img = $this->_convert_to_png($img, $type);
|
668 |
+
if ($img === null) {
|
669 |
+
if ($debug_png) print '!!!conversion to PDF failed!!!';
|
670 |
+
$this->image(Cache::$broken_image, $x, $y, $w, $h, $resolution);
|
671 |
+
break;
|
672 |
+
}
|
673 |
|
674 |
case "png":
|
675 |
if ($debug_png) print '!!!png!!!';
|
753 |
$pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
|
754 |
}
|
755 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
756 |
public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
|
757 |
{
|
758 |
$pdf = $this->_pdf;
|
767 |
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
768 |
}
|
769 |
|
|
|
|
|
|
|
770 |
public function javascript($code)
|
771 |
{
|
772 |
$this->_pdf->addJavascript($code);
|
774 |
|
775 |
//........................................................................
|
776 |
|
|
|
|
|
|
|
|
|
|
|
777 |
public function add_named_dest($anchorname)
|
778 |
{
|
779 |
$this->_pdf->addDestination($anchorname, "Fit");
|
780 |
}
|
781 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
782 |
public function add_link($url, $x, $y, $width, $height)
|
783 |
{
|
784 |
$y = $this->y($y) - $height;
|
790 |
$this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
|
791 |
}
|
792 |
} else {
|
793 |
+
$this->_pdf->addLink($url, $x, $y, $x + $width, $y + $height);
|
794 |
}
|
795 |
}
|
796 |
|
797 |
/**
|
798 |
+
* @throws FontNotFoundException
|
|
|
|
|
|
|
|
|
|
|
799 |
*/
|
800 |
+
public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
|
801 |
{
|
802 |
$this->_pdf->selectFont($font, '', true, $this->_dompdf->getOptions()->getIsFontSubsettingEnabled());
|
803 |
return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
|
804 |
}
|
805 |
|
806 |
/**
|
|
|
|
|
|
|
807 |
* @throws FontNotFoundException
|
808 |
*/
|
809 |
public function get_font_height($font, $size)
|
821 |
}*/
|
822 |
|
823 |
/**
|
824 |
+
* @throws FontNotFoundException
|
|
|
|
|
825 |
*/
|
826 |
public function get_font_baseline($font, $size)
|
827 |
{
|
830 |
}
|
831 |
|
832 |
/**
|
833 |
+
* Processes a callback or script on every page.
|
834 |
*
|
835 |
+
* The callback function receives the four parameters `int $pageNumber`,
|
836 |
+
* `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
|
837 |
+
* that order. If a script is passed as string, the variables `$PAGE_NUM`,
|
838 |
+
* `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing
|
839 |
+
* a script as string is deprecated and will be removed in a future version.
|
840 |
*
|
841 |
+
* This function can be used to add page numbers to all pages after the
|
842 |
+
* first one, for example.
|
843 |
*
|
844 |
+
* @param callable|string $callback The callback function or PHP script to process on every page
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
845 |
*/
|
846 |
+
public function page_script($callback): void
|
847 |
+
{
|
848 |
+
if (is_string($callback)) {
|
849 |
+
$this->processPageScript(function (
|
850 |
+
int $PAGE_NUM,
|
851 |
+
int $PAGE_COUNT,
|
852 |
+
self $pdf,
|
853 |
+
FontMetrics $fontMetrics
|
854 |
+
) use ($callback) {
|
855 |
+
eval($callback);
|
856 |
+
});
|
857 |
+
return;
|
858 |
+
}
|
859 |
+
|
860 |
+
$this->processPageScript($callback);
|
861 |
+
}
|
862 |
+
|
863 |
public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
|
864 |
{
|
865 |
+
$this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) {
|
866 |
+
$text = str_replace(
|
867 |
+
["{PAGE_NUM}", "{PAGE_COUNT}"],
|
868 |
+
[$pageNumber, $pageCount],
|
869 |
+
$text
|
870 |
+
);
|
871 |
+
$this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
|
872 |
+
});
|
873 |
}
|
874 |
|
875 |
+
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
876 |
{
|
877 |
+
$this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) {
|
878 |
+
$this->line($x1, $y1, $x2, $y2, $color, $width, $style);
|
879 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
880 |
}
|
881 |
|
882 |
/**
|
892 |
return $ret;
|
893 |
}
|
894 |
|
895 |
+
protected function processPageScript(callable $callback): void
|
|
|
|
|
|
|
896 |
{
|
897 |
+
$pageNumber = 1;
|
|
|
|
|
|
|
|
|
|
|
898 |
|
899 |
foreach ($this->_pages as $pid) {
|
900 |
$this->reopen_object($pid);
|
901 |
|
902 |
+
$fontMetrics = $this->_dompdf->getFontMetrics();
|
903 |
+
$callback($pageNumber, $this->_page_count, $this, $fontMetrics);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
904 |
|
905 |
$this->close_object();
|
906 |
+
$pageNumber++;
|
907 |
}
|
908 |
}
|
909 |
|
|
|
|
|
|
|
|
|
|
|
|
|
910 |
public function stream($filename = "document.pdf", $options = [])
|
911 |
{
|
912 |
if (headers_sent()) {
|
916 |
if (!isset($options["compress"])) $options["compress"] = true;
|
917 |
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
918 |
|
|
|
|
|
919 |
$debug = !$options['compress'];
|
920 |
$tmp = ltrim($this->_pdf->output($debug));
|
921 |
|
931 |
flush();
|
932 |
}
|
933 |
|
|
|
|
|
|
|
|
|
|
|
|
|
934 |
public function output($options = [])
|
935 |
{
|
936 |
if (!isset($options["compress"])) $options["compress"] = true;
|
937 |
|
|
|
|
|
938 |
$debug = !$options['compress'];
|
939 |
|
940 |
return $this->_pdf->output($debug);
|
vendor/dompdf/dompdf/src/Adapter/GD.php
CHANGED
@@ -10,8 +10,8 @@ namespace Dompdf\Adapter;
|
|
10 |
|
11 |
use Dompdf\Canvas;
|
12 |
use Dompdf\Dompdf;
|
13 |
-
use Dompdf\Image\Cache;
|
14 |
use Dompdf\Helpers;
|
|
|
15 |
|
16 |
/**
|
17 |
* Image rendering interface
|
@@ -130,29 +130,24 @@ class GD implements Canvas
|
|
130 |
const FONT_SCALE = 0.75;
|
131 |
|
132 |
/**
|
133 |
-
*
|
134 |
-
*
|
135 |
-
* @param
|
136 |
-
* @param
|
137 |
-
* @param
|
138 |
-
* @param
|
139 |
-
* @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
|
140 |
*/
|
141 |
-
public function __construct($
|
142 |
{
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
$size = CPDF::$PAPER_SIZES[$size];
|
149 |
-
} else {
|
150 |
-
$size = CPDF::$PAPER_SIZES["letter"];
|
151 |
-
}
|
152 |
}
|
153 |
|
154 |
if (strtolower($orientation) === "landscape") {
|
155 |
-
|
156 |
}
|
157 |
|
158 |
if ($dompdf === null) {
|
@@ -190,9 +185,6 @@ class GD implements Canvas
|
|
190 |
$this->new_page();
|
191 |
}
|
192 |
|
193 |
-
/**
|
194 |
-
* @return Dompdf
|
195 |
-
*/
|
196 |
public function get_dompdf()
|
197 |
{
|
198 |
return $this->_dompdf;
|
@@ -228,21 +220,11 @@ class GD implements Canvas
|
|
228 |
return round($this->_height / $this->_aa_factor);
|
229 |
}
|
230 |
|
231 |
-
/**
|
232 |
-
* Returns the current page number
|
233 |
-
*
|
234 |
-
* @return int
|
235 |
-
*/
|
236 |
public function get_page_number()
|
237 |
{
|
238 |
return $this->_page_number;
|
239 |
}
|
240 |
|
241 |
-
/**
|
242 |
-
* Returns the total number of pages in the document
|
243 |
-
*
|
244 |
-
* @return int
|
245 |
-
*/
|
246 |
public function get_page_count()
|
247 |
{
|
248 |
return $this->_page_count;
|
@@ -258,23 +240,12 @@ class GD implements Canvas
|
|
258 |
$this->_page_number = $num;
|
259 |
}
|
260 |
|
261 |
-
/**
|
262 |
-
* Sets the page count
|
263 |
-
*
|
264 |
-
* @param int $count
|
265 |
-
*/
|
266 |
public function set_page_count($count)
|
267 |
{
|
268 |
$this->_page_count = $count;
|
269 |
}
|
270 |
|
271 |
-
|
272 |
-
* Sets the opacity
|
273 |
-
*
|
274 |
-
* @param $opacity
|
275 |
-
* @param $mode
|
276 |
-
*/
|
277 |
-
public function set_opacity($opacity, $mode = "Normal")
|
278 |
{
|
279 |
// FIXME
|
280 |
}
|
@@ -284,7 +255,7 @@ class GD implements Canvas
|
|
284 |
* previously allocated colors in $this->_colors.
|
285 |
*
|
286 |
* @param array $color The new current color
|
287 |
-
* @return int
|
288 |
*/
|
289 |
protected function _allocate_color($color)
|
290 |
{
|
@@ -342,30 +313,61 @@ class GD implements Canvas
|
|
342 |
* Scales value down from the current canvas DPI to 72 DPI
|
343 |
*
|
344 |
* @param float $length
|
345 |
-
* @return
|
346 |
*/
|
347 |
protected function _downscale($length)
|
348 |
{
|
349 |
return round(($length / $this->dpi * 72) / $this->_aa_factor);
|
350 |
}
|
351 |
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
368 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
369 |
|
370 |
// Scale by the AA factor and DPI
|
371 |
$x1 = $this->_upscale($x1);
|
@@ -378,34 +380,7 @@ class GD implements Canvas
|
|
378 |
|
379 |
// Convert the style array if required
|
380 |
if (is_array($style) && count($style) > 0) {
|
381 |
-
$gd_style =
|
382 |
-
|
383 |
-
if (count($style) == 1) {
|
384 |
-
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
|
385 |
-
$gd_style[] = $c;
|
386 |
-
}
|
387 |
-
|
388 |
-
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
|
389 |
-
$gd_style[] = $this->_bg_color;
|
390 |
-
}
|
391 |
-
} else {
|
392 |
-
$i = 0;
|
393 |
-
foreach ($style as $length) {
|
394 |
-
if ($i % 2 == 0) {
|
395 |
-
// 'On' pattern
|
396 |
-
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
|
397 |
-
$gd_style[] = $c;
|
398 |
-
}
|
399 |
-
|
400 |
-
} else {
|
401 |
-
// Off pattern
|
402 |
-
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
|
403 |
-
$gd_style[] = $this->_bg_color;
|
404 |
-
}
|
405 |
-
}
|
406 |
-
$i++;
|
407 |
-
}
|
408 |
-
}
|
409 |
|
410 |
if (!empty($gd_style)) {
|
411 |
imagesetstyle($this->get_image(), $gd_style);
|
@@ -418,39 +393,59 @@ class GD implements Canvas
|
|
418 |
imageline($this->get_image(), $x1, $y1, $x2, $y2, $c);
|
419 |
}
|
420 |
|
421 |
-
|
422 |
-
* @param float $x1
|
423 |
-
* @param float $y1
|
424 |
-
* @param float $r1
|
425 |
-
* @param float $r2
|
426 |
-
* @param float $astart
|
427 |
-
* @param float $aend
|
428 |
-
* @param array $color
|
429 |
-
* @param float $width
|
430 |
-
* @param array $style
|
431 |
-
*/
|
432 |
-
public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = [])
|
433 |
{
|
434 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
435 |
}
|
436 |
|
437 |
-
|
438 |
-
* Draws a rectangle at x1,y1 with width w and height h
|
439 |
-
*
|
440 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
441 |
-
* See {@link Cpdf::setLineStyle()} for a description of the $style
|
442 |
-
* parameter (aka dash)
|
443 |
-
*
|
444 |
-
* @param float $x1
|
445 |
-
* @param float $y1
|
446 |
-
* @param float $w
|
447 |
-
* @param float $h
|
448 |
-
* @param array $color
|
449 |
-
* @param float $width
|
450 |
-
* @param array $style
|
451 |
-
*/
|
452 |
-
public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null)
|
453 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
454 |
|
455 |
// Scale by the AA factor and DPI
|
456 |
$x1 = $this->_upscale($x1);
|
@@ -463,13 +458,7 @@ class GD implements Canvas
|
|
463 |
|
464 |
// Convert the style array if required
|
465 |
if (is_array($style) && count($style) > 0) {
|
466 |
-
$gd_style =
|
467 |
-
|
468 |
-
foreach ($style as $length) {
|
469 |
-
for ($i = 0; $i < $length; $i++) {
|
470 |
-
$gd_style[] = $c;
|
471 |
-
}
|
472 |
-
}
|
473 |
|
474 |
if (!empty($gd_style)) {
|
475 |
imagesetstyle($this->get_image(), $gd_style);
|
@@ -479,20 +468,18 @@ class GD implements Canvas
|
|
479 |
|
480 |
imagesetthickness($this->get_image(), $width);
|
481 |
|
482 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
483 |
}
|
484 |
|
485 |
-
/**
|
486 |
-
* Draws a filled rectangle at x1,y1 with width w and height h
|
487 |
-
*
|
488 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
489 |
-
*
|
490 |
-
* @param float $x1
|
491 |
-
* @param float $y1
|
492 |
-
* @param float $w
|
493 |
-
* @param float $h
|
494 |
-
* @param array $color
|
495 |
-
*/
|
496 |
public function filled_rectangle($x1, $y1, $w, $h, $color)
|
497 |
{
|
498 |
// Scale by the AA factor and DPI
|
@@ -506,14 +493,6 @@ class GD implements Canvas
|
|
506 |
imagefilledrectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
|
507 |
}
|
508 |
|
509 |
-
/**
|
510 |
-
* Starts a clipping rectangle at x1,y1 with width w and height h
|
511 |
-
*
|
512 |
-
* @param float $x1
|
513 |
-
* @param float $y1
|
514 |
-
* @param float $w
|
515 |
-
* @param float $h
|
516 |
-
*/
|
517 |
public function clipping_rectangle($x1, $y1, $w, $h)
|
518 |
{
|
519 |
// @todo
|
@@ -524,127 +503,65 @@ class GD implements Canvas
|
|
524 |
// @todo
|
525 |
}
|
526 |
|
527 |
-
|
528 |
-
|
529 |
-
|
|
|
|
|
530 |
public function clipping_end()
|
531 |
{
|
532 |
// @todo
|
533 |
}
|
534 |
|
535 |
-
/**
|
536 |
-
*
|
537 |
-
*/
|
538 |
public function save()
|
539 |
{
|
540 |
$this->get_dompdf()->getOptions()->setDpi(72);
|
541 |
}
|
542 |
|
543 |
-
/**
|
544 |
-
*
|
545 |
-
*/
|
546 |
public function restore()
|
547 |
{
|
548 |
$this->get_dompdf()->getOptions()->setDpi($this->dpi);
|
549 |
}
|
550 |
|
551 |
-
/**
|
552 |
-
* @param $angle
|
553 |
-
* @param $x
|
554 |
-
* @param $y
|
555 |
-
*/
|
556 |
public function rotate($angle, $x, $y)
|
557 |
{
|
558 |
// @todo
|
559 |
}
|
560 |
|
561 |
-
/**
|
562 |
-
* @param $angle_x
|
563 |
-
* @param $angle_y
|
564 |
-
* @param $x
|
565 |
-
* @param $y
|
566 |
-
*/
|
567 |
public function skew($angle_x, $angle_y, $x, $y)
|
568 |
{
|
569 |
// @todo
|
570 |
}
|
571 |
|
572 |
-
/**
|
573 |
-
* @param $s_x
|
574 |
-
* @param $s_y
|
575 |
-
* @param $x
|
576 |
-
* @param $y
|
577 |
-
*/
|
578 |
public function scale($s_x, $s_y, $x, $y)
|
579 |
{
|
580 |
// @todo
|
581 |
}
|
582 |
|
583 |
-
/**
|
584 |
-
* @param $t_x
|
585 |
-
* @param $t_y
|
586 |
-
*/
|
587 |
public function translate($t_x, $t_y)
|
588 |
{
|
589 |
// @todo
|
590 |
}
|
591 |
|
592 |
-
/**
|
593 |
-
* @param $a
|
594 |
-
* @param $b
|
595 |
-
* @param $c
|
596 |
-
* @param $d
|
597 |
-
* @param $e
|
598 |
-
* @param $f
|
599 |
-
*/
|
600 |
public function transform($a, $b, $c, $d, $e, $f)
|
601 |
{
|
602 |
// @todo
|
603 |
}
|
604 |
|
605 |
-
|
606 |
-
* Draws a polygon
|
607 |
-
*
|
608 |
-
* The polygon is formed by joining all the points stored in the $points
|
609 |
-
* array. $points has the following structure:
|
610 |
-
* <code>
|
611 |
-
* array(0 => x1,
|
612 |
-
* 1 => y1,
|
613 |
-
* 2 => x2,
|
614 |
-
* 3 => y2,
|
615 |
-
* ...
|
616 |
-
* );
|
617 |
-
* </code>
|
618 |
-
*
|
619 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
620 |
-
* See {@link Cpdf::setLineStyle()} for a description of the $style
|
621 |
-
* parameter (aka dash)
|
622 |
-
*
|
623 |
-
* @param array $points
|
624 |
-
* @param array $color
|
625 |
-
* @param float $width
|
626 |
-
* @param array $style
|
627 |
-
* @param bool $fill Fills the polygon if true
|
628 |
-
*/
|
629 |
-
public function polygon($points, $color, $width = null, $style = null, $fill = false)
|
630 |
{
|
631 |
-
|
632 |
// Scale each point by the AA factor and DPI
|
633 |
foreach (array_keys($points) as $i) {
|
634 |
$points[$i] = $this->_upscale($points[$i]);
|
635 |
}
|
636 |
|
|
|
|
|
637 |
$c = $this->_allocate_color($color);
|
638 |
|
639 |
// Convert the style array if required
|
640 |
-
if (is_array($style) && count($style) > 0 && !$fill) {
|
641 |
-
$gd_style =
|
642 |
-
|
643 |
-
foreach ($style as $length) {
|
644 |
-
for ($i = 0; $i < $length; $i++) {
|
645 |
-
$gd_style[] = $c;
|
646 |
-
}
|
647 |
-
}
|
648 |
|
649 |
if (!empty($gd_style)) {
|
650 |
imagesetstyle($this->get_image(), $gd_style);
|
@@ -652,7 +569,7 @@ class GD implements Canvas
|
|
652 |
}
|
653 |
}
|
654 |
|
655 |
-
imagesetthickness($this->get_image(), isset($width) ?
|
656 |
|
657 |
if ($fill) {
|
658 |
imagefilledpolygon($this->get_image(), $points, $c);
|
@@ -661,39 +578,19 @@ class GD implements Canvas
|
|
661 |
}
|
662 |
}
|
663 |
|
664 |
-
|
665 |
-
* Draws a circle at $x,$y with radius $r
|
666 |
-
*
|
667 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
668 |
-
* See {@link Cpdf::setLineStyle()} for a description of the $style
|
669 |
-
* parameter (aka dash)
|
670 |
-
*
|
671 |
-
* @param float $x
|
672 |
-
* @param float $y
|
673 |
-
* @param float $r
|
674 |
-
* @param array $color
|
675 |
-
* @param float $width
|
676 |
-
* @param array $style
|
677 |
-
* @param bool $fill Fills the circle if true
|
678 |
-
*/
|
679 |
-
public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false)
|
680 |
{
|
681 |
// Scale by the AA factor and DPI
|
682 |
$x = $this->_upscale($x);
|
683 |
$y = $this->_upscale($y);
|
684 |
$d = $this->_upscale(2 * $r);
|
|
|
685 |
|
686 |
$c = $this->_allocate_color($color);
|
687 |
|
688 |
// Convert the style array if required
|
689 |
-
if (is_array($style) && count($style) > 0 && !$fill) {
|
690 |
-
$gd_style =
|
691 |
-
|
692 |
-
foreach ($style as $length) {
|
693 |
-
for ($i = 0; $i < $length; $i++) {
|
694 |
-
$gd_style[] = $c;
|
695 |
-
}
|
696 |
-
}
|
697 |
|
698 |
if (!empty($gd_style)) {
|
699 |
imagesetstyle($this->get_image(), $gd_style);
|
@@ -701,7 +598,7 @@ class GD implements Canvas
|
|
701 |
}
|
702 |
}
|
703 |
|
704 |
-
imagesetthickness($this->get_image(), isset($width) ?
|
705 |
|
706 |
if ($fill) {
|
707 |
imagefilledellipse($this->get_image(), $x, $y, $d, $d, $c);
|
@@ -711,24 +608,11 @@ class GD implements Canvas
|
|
711 |
}
|
712 |
|
713 |
/**
|
714 |
-
* Add an image to the pdf.
|
715 |
-
* The image is placed at the specified x and y coordinates with the
|
716 |
-
* given width and height.
|
717 |
-
*
|
718 |
-
* @param string $img_url the path to the image
|
719 |
-
* @param float $x x position
|
720 |
-
* @param float $y y position
|
721 |
-
* @param int $w width (in pixels)
|
722 |
-
* @param int $h height (in pixels)
|
723 |
-
* @param string $resolution
|
724 |
-
* @return void
|
725 |
-
*
|
726 |
* @throws \Exception
|
727 |
-
* @internal param string $img_type the type (e.g. extension) of the image
|
728 |
*/
|
729 |
-
public function image($
|
730 |
{
|
731 |
-
$img_type = Cache::detect_type($
|
732 |
|
733 |
if (!$img_type) {
|
734 |
return;
|
@@ -737,11 +621,11 @@ class GD implements Canvas
|
|
737 |
$func_name = "imagecreatefrom$img_type";
|
738 |
if (!function_exists($func_name)) {
|
739 |
if (!method_exists(Helpers::class, $func_name)) {
|
740 |
-
throw new \Exception("Function $func_name() not found. Cannot convert $img_type image: $
|
741 |
}
|
742 |
$func_name = [Helpers::class, $func_name];
|
743 |
}
|
744 |
-
$src = @call_user_func($func_name, $
|
745 |
|
746 |
if (!$src) {
|
747 |
return; // Probably should add to $_dompdf_errors or whatever here
|
@@ -760,22 +644,6 @@ class GD implements Canvas
|
|
760 |
imagecopyresampled($this->get_image(), $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
|
761 |
}
|
762 |
|
763 |
-
/**
|
764 |
-
* Writes text at the specified x and y coordinates
|
765 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
766 |
-
*
|
767 |
-
* @param float $x
|
768 |
-
* @param float $y
|
769 |
-
* @param string $text the text to write
|
770 |
-
* @param string $font the font file to use
|
771 |
-
* @param float $size the font size, in points
|
772 |
-
* @param array $color
|
773 |
-
* @param float $word_spacing word spacing adjustment
|
774 |
-
* @param float $char_spacing
|
775 |
-
* @param float $angle Text angle
|
776 |
-
*
|
777 |
-
* @return void
|
778 |
-
*/
|
779 |
public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0)
|
780 |
{
|
781 |
// Scale by the AA factor and DPI
|
@@ -805,61 +673,26 @@ class GD implements Canvas
|
|
805 |
// Not implemented
|
806 |
}
|
807 |
|
808 |
-
/**
|
809 |
-
* Add a named destination (similar to <a name="foo">...</a> in html)
|
810 |
-
*
|
811 |
-
* @param string $anchorname The name of the named destination
|
812 |
-
*/
|
813 |
public function add_named_dest($anchorname)
|
814 |
{
|
815 |
// Not implemented
|
816 |
}
|
817 |
|
818 |
-
/**
|
819 |
-
* Add a link to the pdf
|
820 |
-
*
|
821 |
-
* @param string $url The url to link to
|
822 |
-
* @param float $x The x position of the link
|
823 |
-
* @param float $y The y position of the link
|
824 |
-
* @param float $width The width of the link
|
825 |
-
* @param float $height The height of the link
|
826 |
-
*/
|
827 |
public function add_link($url, $x, $y, $width, $height)
|
828 |
{
|
829 |
// Not implemented
|
830 |
}
|
831 |
|
832 |
-
|
833 |
-
* Add meta information to the PDF
|
834 |
-
*
|
835 |
-
* @param string $label label of the value (Creator, Producer, etc.)
|
836 |
-
* @param string $value the text to set
|
837 |
-
*/
|
838 |
-
public function add_info($label, $value)
|
839 |
{
|
840 |
// N/A
|
841 |
}
|
842 |
|
843 |
-
/**
|
844 |
-
* @param string $view
|
845 |
-
* @param array $options
|
846 |
-
*/
|
847 |
public function set_default_view($view, $options = [])
|
848 |
{
|
849 |
// N/A
|
850 |
}
|
851 |
|
852 |
-
/**
|
853 |
-
* Calculates text size, in points
|
854 |
-
*
|
855 |
-
* @param string $text the text to be sized
|
856 |
-
* @param string $font the desired font
|
857 |
-
* @param float $size the desired font size
|
858 |
-
* @param float $word_spacing word spacing, if any
|
859 |
-
* @param float $char_spacing char spacing, if any
|
860 |
-
*
|
861 |
-
* @return float
|
862 |
-
*/
|
863 |
public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
|
864 |
{
|
865 |
$font = $this->get_ttf_file($font);
|
@@ -913,13 +746,6 @@ class GD implements Canvas
|
|
913 |
return $font;
|
914 |
}
|
915 |
|
916 |
-
/**
|
917 |
-
* Calculates font height, in points
|
918 |
-
*
|
919 |
-
* @param string $font
|
920 |
-
* @param float $size
|
921 |
-
* @return int
|
922 |
-
*/
|
923 |
public function get_font_height($font, $size)
|
924 |
{
|
925 |
$size = $this->_upscale($size) * self::FONT_SCALE;
|
@@ -929,6 +755,12 @@ class GD implements Canvas
|
|
929 |
return $this->_downscale($height);
|
930 |
}
|
931 |
|
|
|
|
|
|
|
|
|
|
|
|
|
932 |
protected function get_font_height_actual($font, $size)
|
933 |
{
|
934 |
$font = $this->get_ttf_file($font);
|
@@ -939,22 +771,12 @@ class GD implements Canvas
|
|
939 |
return ($y2 - $y1) * $ratio;
|
940 |
}
|
941 |
|
942 |
-
/**
|
943 |
-
* @param string $font
|
944 |
-
* @param float $size
|
945 |
-
* @return float
|
946 |
-
*/
|
947 |
public function get_font_baseline($font, $size)
|
948 |
{
|
949 |
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
|
950 |
return $this->get_font_height($font, $size) / $ratio;
|
951 |
}
|
952 |
|
953 |
-
/**
|
954 |
-
* Starts a new page
|
955 |
-
*
|
956 |
-
* Subsequent drawing operations will appear on the new page.
|
957 |
-
*/
|
958 |
public function new_page()
|
959 |
{
|
960 |
$this->_page_number++;
|
@@ -985,7 +807,7 @@ class GD implements Canvas
|
|
985 |
// N/A
|
986 |
}
|
987 |
|
988 |
-
public function page_script($callback)
|
989 |
{
|
990 |
// N/A
|
991 |
}
|
@@ -995,7 +817,7 @@ class GD implements Canvas
|
|
995 |
// N/A
|
996 |
}
|
997 |
|
998 |
-
public function page_line()
|
999 |
{
|
1000 |
// N/A
|
1001 |
}
|
@@ -1004,7 +826,7 @@ class GD implements Canvas
|
|
1004 |
* Streams the image to the client.
|
1005 |
*
|
1006 |
* @param string $filename The filename to present to the client.
|
1007 |
-
* @param array
|
1008 |
* 'page' => Number of the page to output (defaults to the first); 'Attachment': 1 or 0 (default 1).
|
1009 |
*/
|
1010 |
public function stream($filename, $options = [])
|
10 |
|
11 |
use Dompdf\Canvas;
|
12 |
use Dompdf\Dompdf;
|
|
|
13 |
use Dompdf\Helpers;
|
14 |
+
use Dompdf\Image\Cache;
|
15 |
|
16 |
/**
|
17 |
* Image rendering interface
|
130 |
const FONT_SCALE = 0.75;
|
131 |
|
132 |
/**
|
133 |
+
* @param string|float[] $paper The paper size to use as either a standard paper size (see {@link CPDF::$PAPER_SIZES}) or
|
134 |
+
* an array of the form `[x1, y1, x2, y2]` (typically `[0, 0, width, height]`).
|
135 |
+
* @param string $orientation The paper orientation, either `portrait` or `landscape`.
|
136 |
+
* @param Dompdf $dompdf The Dompdf instance.
|
137 |
+
* @param float $aa_factor Anti-aliasing factor, 1 for no AA
|
138 |
+
* @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
|
|
|
139 |
*/
|
140 |
+
public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null, $aa_factor = 1.0, $bg_color = [1, 1, 1, 0])
|
141 |
{
|
142 |
+
if (is_array($paper)) {
|
143 |
+
$size = array_map("floatval", $paper);
|
144 |
+
} else {
|
145 |
+
$paper = strtolower($paper);
|
146 |
+
$size = CPDF::$PAPER_SIZES[$paper] ?? CPDF::$PAPER_SIZES["letter"];
|
|
|
|
|
|
|
|
|
147 |
}
|
148 |
|
149 |
if (strtolower($orientation) === "landscape") {
|
150 |
+
[$size[2], $size[3]] = [$size[3], $size[2]];
|
151 |
}
|
152 |
|
153 |
if ($dompdf === null) {
|
185 |
$this->new_page();
|
186 |
}
|
187 |
|
|
|
|
|
|
|
188 |
public function get_dompdf()
|
189 |
{
|
190 |
return $this->_dompdf;
|
220 |
return round($this->_height / $this->_aa_factor);
|
221 |
}
|
222 |
|
|
|
|
|
|
|
|
|
|
|
223 |
public function get_page_number()
|
224 |
{
|
225 |
return $this->_page_number;
|
226 |
}
|
227 |
|
|
|
|
|
|
|
|
|
|
|
228 |
public function get_page_count()
|
229 |
{
|
230 |
return $this->_page_count;
|
240 |
$this->_page_number = $num;
|
241 |
}
|
242 |
|
|
|
|
|
|
|
|
|
|
|
243 |
public function set_page_count($count)
|
244 |
{
|
245 |
$this->_page_count = $count;
|
246 |
}
|
247 |
|
248 |
+
public function set_opacity(float $opacity, string $mode = "Normal"): void
|
|
|
|
|
|
|
|
|
|
|
|
|
249 |
{
|
250 |
// FIXME
|
251 |
}
|
255 |
* previously allocated colors in $this->_colors.
|
256 |
*
|
257 |
* @param array $color The new current color
|
258 |
+
* @return int The allocated color
|
259 |
*/
|
260 |
protected function _allocate_color($color)
|
261 |
{
|
313 |
* Scales value down from the current canvas DPI to 72 DPI
|
314 |
*
|
315 |
* @param float $length
|
316 |
+
* @return float
|
317 |
*/
|
318 |
protected function _downscale($length)
|
319 |
{
|
320 |
return round(($length / $this->dpi * 72) / $this->_aa_factor);
|
321 |
}
|
322 |
|
323 |
+
protected function convertStyle(array $style, int $color, int $width): array
|
324 |
+
{
|
325 |
+
$gdStyle = [];
|
326 |
+
|
327 |
+
if (count($style) === 1) {
|
328 |
+
$style[] = $style[0];
|
329 |
+
}
|
330 |
+
|
331 |
+
foreach ($style as $index => $s) {
|
332 |
+
$d = $this->_upscale($s);
|
333 |
+
|
334 |
+
for ($i = 0; $i < $d; $i++) {
|
335 |
+
for ($j = 0; $j < $width; $j++) {
|
336 |
+
$gdStyle[] = $index % 2 === 0
|
337 |
+
? $color
|
338 |
+
: IMG_COLOR_TRANSPARENT;
|
339 |
+
}
|
340 |
+
}
|
341 |
+
}
|
342 |
+
|
343 |
+
return $gdStyle;
|
344 |
+
}
|
345 |
+
|
346 |
+
public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
|
347 |
{
|
348 |
+
// Account for the fact that round and square caps are expected to
|
349 |
+
// extend outwards
|
350 |
+
if ($cap === "round" || $cap === "square") {
|
351 |
+
// Shift line by half width
|
352 |
+
$w = $width / 2;
|
353 |
+
$a = $x2 - $x1;
|
354 |
+
$b = $y2 - $y1;
|
355 |
+
$c = sqrt($a ** 2 + $b ** 2);
|
356 |
+
$dx = $a * $w / $c;
|
357 |
+
$dy = $b * $w / $c;
|
358 |
+
|
359 |
+
$x1 -= $dx;
|
360 |
+
$x2 -= $dx;
|
361 |
+
$y1 -= $dy;
|
362 |
+
$y2 -= $dy;
|
363 |
+
|
364 |
+
// Adapt dash pattern
|
365 |
+
if (is_array($style)) {
|
366 |
+
foreach ($style as $index => &$s) {
|
367 |
+
$s = $index % 2 === 0 ? $s + $width : $s - $width;
|
368 |
+
}
|
369 |
+
}
|
370 |
+
}
|
371 |
|
372 |
// Scale by the AA factor and DPI
|
373 |
$x1 = $this->_upscale($x1);
|
380 |
|
381 |
// Convert the style array if required
|
382 |
if (is_array($style) && count($style) > 0) {
|
383 |
+
$gd_style = $this->convertStyle($style, $c, $width);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
384 |
|
385 |
if (!empty($gd_style)) {
|
386 |
imagesetstyle($this->get_image(), $gd_style);
|
393 |
imageline($this->get_image(), $x1, $y1, $x2, $y2, $c);
|
394 |
}
|
395 |
|
396 |
+
public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
397 |
{
|
398 |
+
// Account for the fact that round and square caps are expected to
|
399 |
+
// extend outwards
|
400 |
+
if ($cap === "round" || $cap === "square") {
|
401 |
+
// Adapt dash pattern
|
402 |
+
if (is_array($style)) {
|
403 |
+
foreach ($style as $index => &$s) {
|
404 |
+
$s = $index % 2 === 0 ? $s + $width : $s - $width;
|
405 |
+
}
|
406 |
+
}
|
407 |
+
}
|
408 |
+
|
409 |
+
// Scale by the AA factor and DPI
|
410 |
+
$x = $this->_upscale($x);
|
411 |
+
$y = $this->_upscale($y);
|
412 |
+
$w = $this->_upscale($r1 * 2);
|
413 |
+
$h = $this->_upscale($r2 * 2);
|
414 |
+
$width = $this->_upscale($width);
|
415 |
+
|
416 |
+
// Adapt angles as imagearc counts clockwise
|
417 |
+
$start = 360 - $aend;
|
418 |
+
$end = 360 - $astart;
|
419 |
+
|
420 |
+
$c = $this->_allocate_color($color);
|
421 |
+
|
422 |
+
// Convert the style array if required
|
423 |
+
if (is_array($style) && count($style) > 0) {
|
424 |
+
$gd_style = $this->convertStyle($style, $c, $width);
|
425 |
+
|
426 |
+
if (!empty($gd_style)) {
|
427 |
+
imagesetstyle($this->get_image(), $gd_style);
|
428 |
+
$c = IMG_COLOR_STYLED;
|
429 |
+
}
|
430 |
+
}
|
431 |
+
|
432 |
+
imagesetthickness($this->get_image(), $width);
|
433 |
+
|
434 |
+
imagearc($this->get_image(), $x, $y, $w, $h, $start, $end, $c);
|
435 |
}
|
436 |
|
437 |
+
public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
438 |
{
|
439 |
+
// Account for the fact that round and square caps are expected to
|
440 |
+
// extend outwards
|
441 |
+
if ($cap === "round" || $cap === "square") {
|
442 |
+
// Adapt dash pattern
|
443 |
+
if (is_array($style)) {
|
444 |
+
foreach ($style as $index => &$s) {
|
445 |
+
$s = $index % 2 === 0 ? $s + $width : $s - $width;
|
446 |
+
}
|
447 |
+
}
|
448 |
+
}
|
449 |
|
450 |
// Scale by the AA factor and DPI
|
451 |
$x1 = $this->_upscale($x1);
|
458 |
|
459 |
// Convert the style array if required
|
460 |
if (is_array($style) && count($style) > 0) {
|
461 |
+
$gd_style = $this->convertStyle($style, $c, $width);
|
|
|
|
|
|
|
|
|
|
|
|
|
462 |
|
463 |
if (!empty($gd_style)) {
|
464 |
imagesetstyle($this->get_image(), $gd_style);
|
468 |
|
469 |
imagesetthickness($this->get_image(), $width);
|
470 |
|
471 |
+
if ($c === IMG_COLOR_STYLED) {
|
472 |
+
imagepolygon($this->get_image(), [
|
473 |
+
$x1, $y1,
|
474 |
+
$x1 + $w, $y1,
|
475 |
+
$x1 + $w, $y1 + $h,
|
476 |
+
$x1, $y1 + $h
|
477 |
+
], $c);
|
478 |
+
} else {
|
479 |
+
imagerectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
|
480 |
+
}
|
481 |
}
|
482 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
483 |
public function filled_rectangle($x1, $y1, $w, $h, $color)
|
484 |
{
|
485 |
// Scale by the AA factor and DPI
|
493 |
imagefilledrectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
|
494 |
}
|
495 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
496 |
public function clipping_rectangle($x1, $y1, $w, $h)
|
497 |
{
|
498 |
// @todo
|
503 |
// @todo
|
504 |
}
|
505 |
|
506 |
+
public function clipping_polygon(array $points): void
|
507 |
+
{
|
508 |
+
// @todo
|
509 |
+
}
|
510 |
+
|
511 |
public function clipping_end()
|
512 |
{
|
513 |
// @todo
|
514 |
}
|
515 |
|
|
|
|
|
|
|
516 |
public function save()
|
517 |
{
|
518 |
$this->get_dompdf()->getOptions()->setDpi(72);
|
519 |
}
|
520 |
|
|
|
|
|
|
|
521 |
public function restore()
|
522 |
{
|
523 |
$this->get_dompdf()->getOptions()->setDpi($this->dpi);
|
524 |
}
|
525 |
|
|
|
|
|
|
|
|
|
|
|
526 |
public function rotate($angle, $x, $y)
|
527 |
{
|
528 |
// @todo
|
529 |
}
|
530 |
|
|
|
|
|
|
|
|
|
|
|
|
|
531 |
public function skew($angle_x, $angle_y, $x, $y)
|
532 |
{
|
533 |
// @todo
|
534 |
}
|
535 |
|
|
|
|
|
|
|
|
|
|
|
|
|
536 |
public function scale($s_x, $s_y, $x, $y)
|
537 |
{
|
538 |
// @todo
|
539 |
}
|
540 |
|
|
|
|
|
|
|
|
|
541 |
public function translate($t_x, $t_y)
|
542 |
{
|
543 |
// @todo
|
544 |
}
|
545 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
546 |
public function transform($a, $b, $c, $d, $e, $f)
|
547 |
{
|
548 |
// @todo
|
549 |
}
|
550 |
|
551 |
+
public function polygon($points, $color, $width = null, $style = [], $fill = false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
552 |
{
|
|
|
553 |
// Scale each point by the AA factor and DPI
|
554 |
foreach (array_keys($points) as $i) {
|
555 |
$points[$i] = $this->_upscale($points[$i]);
|
556 |
}
|
557 |
|
558 |
+
$width = isset($width) ? $this->_upscale($width) : null;
|
559 |
+
|
560 |
$c = $this->_allocate_color($color);
|
561 |
|
562 |
// Convert the style array if required
|
563 |
+
if (is_array($style) && count($style) > 0 && isset($width) && !$fill) {
|
564 |
+
$gd_style = $this->convertStyle($style, $c, $width);
|
|
|
|
|
|
|
|
|
|
|
|
|
565 |
|
566 |
if (!empty($gd_style)) {
|
567 |
imagesetstyle($this->get_image(), $gd_style);
|
569 |
}
|
570 |
}
|
571 |
|
572 |
+
imagesetthickness($this->get_image(), isset($width) ? $width : 0);
|
573 |
|
574 |
if ($fill) {
|
575 |
imagefilledpolygon($this->get_image(), $points, $c);
|
578 |
}
|
579 |
}
|
580 |
|
581 |
+
public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
582 |
{
|
583 |
// Scale by the AA factor and DPI
|
584 |
$x = $this->_upscale($x);
|
585 |
$y = $this->_upscale($y);
|
586 |
$d = $this->_upscale(2 * $r);
|
587 |
+
$width = isset($width) ? $this->_upscale($width) : null;
|
588 |
|
589 |
$c = $this->_allocate_color($color);
|
590 |
|
591 |
// Convert the style array if required
|
592 |
+
if (is_array($style) && count($style) > 0 && isset($width) && !$fill) {
|
593 |
+
$gd_style = $this->convertStyle($style, $c, $width);
|
|
|
|
|
|
|
|
|
|
|
|
|
594 |
|
595 |
if (!empty($gd_style)) {
|
596 |
imagesetstyle($this->get_image(), $gd_style);
|
598 |
}
|
599 |
}
|
600 |
|
601 |
+
imagesetthickness($this->get_image(), isset($width) ? $width : 0);
|
602 |
|
603 |
if ($fill) {
|
604 |
imagefilledellipse($this->get_image(), $x, $y, $d, $d, $c);
|
608 |
}
|
609 |
|
610 |
/**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
611 |
* @throws \Exception
|
|
|
612 |
*/
|
613 |
+
public function image($img, $x, $y, $w, $h, $resolution = "normal")
|
614 |
{
|
615 |
+
$img_type = Cache::detect_type($img, $this->get_dompdf()->getHttpContext());
|
616 |
|
617 |
if (!$img_type) {
|
618 |
return;
|
621 |
$func_name = "imagecreatefrom$img_type";
|
622 |
if (!function_exists($func_name)) {
|
623 |
if (!method_exists(Helpers::class, $func_name)) {
|
624 |
+
throw new \Exception("Function $func_name() not found. Cannot convert $img_type image: $img. Please install the image PHP extension.");
|
625 |
}
|
626 |
$func_name = [Helpers::class, $func_name];
|
627 |
}
|
628 |
+
$src = @call_user_func($func_name, $img);
|
629 |
|
630 |
if (!$src) {
|
631 |
return; // Probably should add to $_dompdf_errors or whatever here
|
644 |
imagecopyresampled($this->get_image(), $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
|
645 |
}
|
646 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
647 |
public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0)
|
648 |
{
|
649 |
// Scale by the AA factor and DPI
|
673 |
// Not implemented
|
674 |
}
|
675 |
|
|
|
|
|
|
|
|
|
|
|
676 |
public function add_named_dest($anchorname)
|
677 |
{
|
678 |
// Not implemented
|
679 |
}
|
680 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
681 |
public function add_link($url, $x, $y, $width, $height)
|
682 |
{
|
683 |
// Not implemented
|
684 |
}
|
685 |
|
686 |
+
public function add_info(string $label, string $value): void
|
|
|
|
|
|
|
|
|
|
|
|
|
687 |
{
|
688 |
// N/A
|
689 |
}
|
690 |
|
|
|
|
|
|
|
|
|
691 |
public function set_default_view($view, $options = [])
|
692 |
{
|
693 |
// N/A
|
694 |
}
|
695 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
696 |
public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
|
697 |
{
|
698 |
$font = $this->get_ttf_file($font);
|
746 |
return $font;
|
747 |
}
|
748 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
749 |
public function get_font_height($font, $size)
|
750 |
{
|
751 |
$size = $this->_upscale($size) * self::FONT_SCALE;
|
755 |
return $this->_downscale($height);
|
756 |
}
|
757 |
|
758 |
+
/**
|
759 |
+
* @param string $font
|
760 |
+
* @param float $size
|
761 |
+
*
|
762 |
+
* @return float
|
763 |
+
*/
|
764 |
protected function get_font_height_actual($font, $size)
|
765 |
{
|
766 |
$font = $this->get_ttf_file($font);
|
771 |
return ($y2 - $y1) * $ratio;
|
772 |
}
|
773 |
|
|
|
|
|
|
|
|
|
|
|
774 |
public function get_font_baseline($font, $size)
|
775 |
{
|
776 |
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
|
777 |
return $this->get_font_height($font, $size) / $ratio;
|
778 |
}
|
779 |
|
|
|
|
|
|
|
|
|
|
|
780 |
public function new_page()
|
781 |
{
|
782 |
$this->_page_number++;
|
807 |
// N/A
|
808 |
}
|
809 |
|
810 |
+
public function page_script($callback): void
|
811 |
{
|
812 |
// N/A
|
813 |
}
|
817 |
// N/A
|
818 |
}
|
819 |
|
820 |
+
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
|
821 |
{
|
822 |
// N/A
|
823 |
}
|
826 |
* Streams the image to the client.
|
827 |
*
|
828 |
* @param string $filename The filename to present to the client.
|
829 |
+
* @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
|
830 |
* 'page' => Number of the page to output (defaults to the first); 'Attachment': 1 or 0 (default 1).
|
831 |
*/
|
832 |
public function stream($filename, $options = [])
|
vendor/dompdf/dompdf/src/Adapter/PDFLib.php
CHANGED
@@ -11,10 +11,10 @@ namespace Dompdf\Adapter;
|
|
11 |
|
12 |
use Dompdf\Canvas;
|
13 |
use Dompdf\Dompdf;
|
14 |
-
use Dompdf\Helpers;
|
15 |
use Dompdf\Exception;
|
|
|
|
|
16 |
use Dompdf\Image\Cache;
|
17 |
-
use Dompdf\PhpEvaluator;
|
18 |
|
19 |
/**
|
20 |
* PDF rendering interface
|
@@ -37,7 +37,7 @@ class PDFLib implements Canvas
|
|
37 |
/**
|
38 |
* Dimensions of paper sizes in points
|
39 |
*
|
40 |
-
* @var array
|
41 |
*/
|
42 |
public static $PAPER_SIZES = []; // Set to Dompdf\Adapter\CPDF::$PAPER_SIZES below.
|
43 |
|
@@ -130,7 +130,7 @@ class PDFLib implements Canvas
|
|
130 |
/**
|
131 |
* The current opacity level
|
132 |
*
|
133 |
-
* @var
|
134 |
*/
|
135 |
protected $_current_opacity;
|
136 |
|
@@ -184,39 +184,23 @@ class PDFLib implements Canvas
|
|
184 |
protected $_page_count;
|
185 |
|
186 |
/**
|
187 |
-
*
|
188 |
-
*
|
189 |
-
* @var array
|
190 |
-
*/
|
191 |
-
protected $_page_text;
|
192 |
-
|
193 |
-
/**
|
194 |
-
* Array of pages for accesing after rendering is initially complete
|
195 |
*
|
196 |
* @var array
|
197 |
*/
|
198 |
protected $_pages;
|
199 |
|
200 |
-
|
201 |
-
* Class constructor
|
202 |
-
*
|
203 |
-
* @param string|array $paper The size of paper to use either a string (see {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}) or
|
204 |
-
* an array(xmin,ymin,xmax,ymax)
|
205 |
-
* @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
|
206 |
-
* @param Dompdf $dompdf
|
207 |
-
*/
|
208 |
-
public function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf = null)
|
209 |
{
|
210 |
if (is_array($paper)) {
|
211 |
-
$size = $paper;
|
212 |
-
} elseif (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) {
|
213 |
-
$size = self::$PAPER_SIZES[mb_strtolower($paper)];
|
214 |
} else {
|
215 |
-
$
|
|
|
216 |
}
|
217 |
|
218 |
-
if (
|
219 |
-
|
220 |
}
|
221 |
|
222 |
$this->_width = $size[2] - $size[0];
|
@@ -235,7 +219,9 @@ class PDFLib implements Canvas
|
|
235 |
$this->setPDFLibParameter("license", $license);
|
236 |
}
|
237 |
|
238 |
-
$this->
|
|
|
|
|
239 |
if ($this->getPDFLibMajorVersion() >= 7) {
|
240 |
$this->setPDFLibParameter("errorpolicy", "return");
|
241 |
// $this->_pdf->set_option('logging={filename=' . \APP_PATH . '/logs/pdflib.log classes={api=1 warning=2}}');
|
@@ -271,16 +257,12 @@ class PDFLib implements Canvas
|
|
271 |
$this->_pdf->begin_page_ext($this->_width, $this->_height, "");
|
272 |
|
273 |
$this->_page_number = $this->_page_count = 1;
|
274 |
-
$this->_page_text = [];
|
275 |
|
276 |
$this->_imgs = [];
|
277 |
$this->_fonts = [];
|
278 |
$this->_objs = [];
|
279 |
}
|
280 |
|
281 |
-
/**
|
282 |
-
* @return Dompdf
|
283 |
-
*/
|
284 |
function get_dompdf()
|
285 |
{
|
286 |
return $this->_dompdf;
|
@@ -314,13 +296,7 @@ class PDFLib implements Canvas
|
|
314 |
return $this->_pdf;
|
315 |
}
|
316 |
|
317 |
-
|
318 |
-
* Add meta information to the PDF
|
319 |
-
*
|
320 |
-
* @param string $label label of the value (Creator, Producter, etc.)
|
321 |
-
* @param string $value the text to set
|
322 |
-
*/
|
323 |
-
public function add_info($label, $value)
|
324 |
{
|
325 |
$this->_pdf->set_info($label, $value);
|
326 |
}
|
@@ -464,33 +440,21 @@ class PDFLib implements Canvas
|
|
464 |
}
|
465 |
}
|
466 |
|
467 |
-
/**
|
468 |
-
* @return float|mixed
|
469 |
-
*/
|
470 |
public function get_width()
|
471 |
{
|
472 |
return $this->_width;
|
473 |
}
|
474 |
|
475 |
-
/**
|
476 |
-
* @return float|mixed
|
477 |
-
*/
|
478 |
public function get_height()
|
479 |
{
|
480 |
return $this->_height;
|
481 |
}
|
482 |
|
483 |
-
/**
|
484 |
-
* @return int
|
485 |
-
*/
|
486 |
public function get_page_number()
|
487 |
{
|
488 |
return $this->_page_number;
|
489 |
}
|
490 |
|
491 |
-
/**
|
492 |
-
* @return int
|
493 |
-
*/
|
494 |
public function get_page_count()
|
495 |
{
|
496 |
return $this->_page_count;
|
@@ -504,9 +468,6 @@ class PDFLib implements Canvas
|
|
504 |
$this->_page_number = (int)$num;
|
505 |
}
|
506 |
|
507 |
-
/**
|
508 |
-
* @param int $count
|
509 |
-
*/
|
510 |
public function set_page_count($count)
|
511 |
{
|
512 |
$this->_page_count = (int)$count;
|
@@ -516,19 +477,25 @@ class PDFLib implements Canvas
|
|
516 |
* Sets the line style
|
517 |
*
|
518 |
* @param float $width
|
519 |
-
* @param
|
520 |
* @param string $join
|
521 |
* @param array $dash
|
522 |
-
*
|
523 |
-
* @return void
|
524 |
*/
|
525 |
protected function _set_line_style($width, $cap, $join, $dash)
|
526 |
{
|
527 |
if (!is_array($dash)) {
|
528 |
-
$dash =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
529 |
}
|
530 |
|
531 |
-
if (count($dash)
|
532 |
$dash[] = $dash[0];
|
533 |
}
|
534 |
|
@@ -684,12 +651,12 @@ class PDFLib implements Canvas
|
|
684 |
/**
|
685 |
* Sets the fill opacity
|
686 |
*
|
687 |
-
* @param $opacity
|
688 |
-
* @param $mode
|
689 |
*/
|
690 |
public function _set_fill_opacity($opacity, $mode = "Normal")
|
691 |
{
|
692 |
-
if ($mode === "Normal" &&
|
693 |
$this->_set_gstate("opacityfill=$opacity");
|
694 |
}
|
695 |
}
|
@@ -697,25 +664,19 @@ class PDFLib implements Canvas
|
|
697 |
/**
|
698 |
* Sets the stroke opacity
|
699 |
*
|
700 |
-
* @param $opacity
|
701 |
-
* @param $mode
|
702 |
*/
|
703 |
public function _set_stroke_opacity($opacity, $mode = "Normal")
|
704 |
{
|
705 |
-
if ($mode === "Normal" &&
|
706 |
$this->_set_gstate("opacitystroke=$opacity");
|
707 |
}
|
708 |
}
|
709 |
|
710 |
-
|
711 |
-
* Sets the opacity
|
712 |
-
*
|
713 |
-
* @param $opacity
|
714 |
-
* @param $mode
|
715 |
-
*/
|
716 |
-
public function set_opacity($opacity, $mode = "Normal")
|
717 |
{
|
718 |
-
if ($mode === "Normal"
|
719 |
$this->_set_gstate("opacityfill=$opacity opacitystroke=$opacity");
|
720 |
$this->_current_opacity = $opacity;
|
721 |
}
|
@@ -874,18 +835,9 @@ class PDFLib implements Canvas
|
|
874 |
return $this->_height - $y;
|
875 |
}
|
876 |
|
877 |
-
|
878 |
-
* @param float $x1
|
879 |
-
* @param float $y1
|
880 |
-
* @param float $x2
|
881 |
-
* @param float $y2
|
882 |
-
* @param array $color
|
883 |
-
* @param float $width
|
884 |
-
* @param array $style
|
885 |
-
*/
|
886 |
-
public function line($x1, $y1, $x2, $y2, $color, $width, $style = null)
|
887 |
{
|
888 |
-
$this->_set_line_style($width,
|
889 |
$this->_set_stroke_color($color);
|
890 |
|
891 |
$y1 = $this->y($y1);
|
@@ -898,62 +850,23 @@ class PDFLib implements Canvas
|
|
898 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
899 |
}
|
900 |
|
901 |
-
|
902 |
-
* Draw line at the specified coordinates on every page.
|
903 |
-
*
|
904 |
-
* See {@link Style::munge_color()} for the format of the colour array.
|
905 |
-
*
|
906 |
-
* @param float $x1
|
907 |
-
* @param float $y1
|
908 |
-
* @param float $x2
|
909 |
-
* @param float $y2
|
910 |
-
* @param array $color
|
911 |
-
* @param float $width
|
912 |
-
* @param array $style optional
|
913 |
-
*/
|
914 |
-
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
|
915 |
{
|
916 |
-
$
|
917 |
-
$this->_page_text[] = compact('_t', 'x1', 'y1', 'x2', 'y2', 'color', 'width', 'style');
|
918 |
-
}
|
919 |
-
|
920 |
-
/**
|
921 |
-
* @param float $x1
|
922 |
-
* @param float $y1
|
923 |
-
* @param float $r1
|
924 |
-
* @param float $r2
|
925 |
-
* @param float $astart
|
926 |
-
* @param float $aend
|
927 |
-
* @param array $color
|
928 |
-
* @param float $width
|
929 |
-
* @param array $style
|
930 |
-
*/
|
931 |
-
public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = [])
|
932 |
-
{
|
933 |
-
$this->_set_line_style($width, "butt", "", $style);
|
934 |
$this->_set_stroke_color($color);
|
935 |
|
936 |
-
$
|
937 |
|
938 |
-
$this->_pdf->arc($
|
939 |
$this->_pdf->stroke();
|
940 |
|
941 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
942 |
}
|
943 |
|
944 |
-
|
945 |
-
* @param float $x1
|
946 |
-
* @param float $y1
|
947 |
-
* @param float $w
|
948 |
-
* @param float $h
|
949 |
-
* @param array $color
|
950 |
-
* @param float $width
|
951 |
-
* @param null $style
|
952 |
-
*/
|
953 |
-
public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null)
|
954 |
{
|
955 |
$this->_set_stroke_color($color);
|
956 |
-
$this->_set_line_style($width,
|
957 |
|
958 |
$y1 = $this->y($y1) - $h;
|
959 |
|
@@ -963,13 +876,6 @@ class PDFLib implements Canvas
|
|
963 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
964 |
}
|
965 |
|
966 |
-
/**
|
967 |
-
* @param float $x1
|
968 |
-
* @param float $y1
|
969 |
-
* @param float $w
|
970 |
-
* @param float $h
|
971 |
-
* @param array $color
|
972 |
-
*/
|
973 |
public function filled_rectangle($x1, $y1, $w, $h, $color)
|
974 |
{
|
975 |
$this->_set_fill_color($color);
|
@@ -982,12 +888,6 @@ class PDFLib implements Canvas
|
|
982 |
$this->_set_fill_opacity($this->_current_opacity, "Normal");
|
983 |
}
|
984 |
|
985 |
-
/**
|
986 |
-
* @param float $x1
|
987 |
-
* @param float $y1
|
988 |
-
* @param float $w
|
989 |
-
* @param float $h
|
990 |
-
*/
|
991 |
public function clipping_rectangle($x1, $y1, $w, $h)
|
992 |
{
|
993 |
$this->_pdf->save();
|
@@ -998,16 +898,6 @@ class PDFLib implements Canvas
|
|
998 |
$this->_pdf->clip();
|
999 |
}
|
1000 |
|
1001 |
-
/**
|
1002 |
-
* @param float $x1
|
1003 |
-
* @param float $y1
|
1004 |
-
* @param float $w
|
1005 |
-
* @param float $h
|
1006 |
-
* @param float $rTL
|
1007 |
-
* @param float $rTR
|
1008 |
-
* @param float $rBR
|
1009 |
-
* @param float $rBL
|
1010 |
-
*/
|
1011 |
public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
1012 |
{
|
1013 |
if ($this->getPDFLibMajorVersion() < 9) {
|
@@ -1027,33 +917,53 @@ class PDFLib implements Canvas
|
|
1027 |
// line: left edge, bottom end
|
1028 |
$path = $this->_pdf->add_path_point($path, 0, 0 + $rBL, "line", "");
|
1029 |
// curve: bottom-left corner
|
1030 |
-
|
|
|
|
|
1031 |
// line: bottom edge, left end
|
1032 |
$path = $this->_pdf->add_path_point($path, 0 - $rBR + $w, 0, "line", "");
|
1033 |
// curve: bottom-right corner
|
1034 |
-
|
|
|
|
|
1035 |
// line: right edge, top end
|
1036 |
$path = $this->_pdf->add_path_point($path, 0 + $w, 0 - $rTR + $h, "line", "");
|
1037 |
// curve: top-right corner
|
1038 |
-
|
|
|
|
|
1039 |
// line: top edge, left end
|
1040 |
$path = $this->_pdf->add_path_point($path, 0 + $rTL, 0 + $h, "line", "");
|
1041 |
// curve: top-left corner
|
1042 |
-
|
|
|
|
|
1043 |
$this->_pdf->draw_path($path, $x1, $this->_height-$y1-$h, "clip=true");
|
1044 |
}
|
1045 |
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1049 |
public function clipping_end()
|
1050 |
{
|
1051 |
$this->_pdf->restore();
|
1052 |
}
|
1053 |
|
1054 |
-
/**
|
1055 |
-
*
|
1056 |
-
*/
|
1057 |
public function save()
|
1058 |
{
|
1059 |
$this->_pdf->save();
|
@@ -1064,11 +974,6 @@ class PDFLib implements Canvas
|
|
1064 |
$this->_pdf->restore();
|
1065 |
}
|
1066 |
|
1067 |
-
/**
|
1068 |
-
* @param $angle
|
1069 |
-
* @param $x
|
1070 |
-
* @param $y
|
1071 |
-
*/
|
1072 |
public function rotate($angle, $x, $y)
|
1073 |
{
|
1074 |
$pdf = $this->_pdf;
|
@@ -1077,12 +982,6 @@ class PDFLib implements Canvas
|
|
1077 |
$pdf->translate(-$x, -$this->_height + $y);
|
1078 |
}
|
1079 |
|
1080 |
-
/**
|
1081 |
-
* @param $angle_x
|
1082 |
-
* @param $angle_y
|
1083 |
-
* @param $x
|
1084 |
-
* @param $y
|
1085 |
-
*/
|
1086 |
public function skew($angle_x, $angle_y, $x, $y)
|
1087 |
{
|
1088 |
$pdf = $this->_pdf;
|
@@ -1091,12 +990,6 @@ class PDFLib implements Canvas
|
|
1091 |
$pdf->translate(-$x, -$this->_height + $y);
|
1092 |
}
|
1093 |
|
1094 |
-
/**
|
1095 |
-
* @param $s_x
|
1096 |
-
* @param $s_y
|
1097 |
-
* @param $x
|
1098 |
-
* @param $y
|
1099 |
-
*/
|
1100 |
public function scale($s_x, $s_y, $x, $y)
|
1101 |
{
|
1102 |
$pdf = $this->_pdf;
|
@@ -1105,36 +998,17 @@ class PDFLib implements Canvas
|
|
1105 |
$pdf->translate(-$x, -$this->_height + $y);
|
1106 |
}
|
1107 |
|
1108 |
-
/**
|
1109 |
-
* @param $t_x
|
1110 |
-
* @param $t_y
|
1111 |
-
*/
|
1112 |
public function translate($t_x, $t_y)
|
1113 |
{
|
1114 |
$this->_pdf->translate($t_x, -$t_y);
|
1115 |
}
|
1116 |
|
1117 |
-
/**
|
1118 |
-
* @param $a
|
1119 |
-
* @param $b
|
1120 |
-
* @param $c
|
1121 |
-
* @param $d
|
1122 |
-
* @param $e
|
1123 |
-
* @param $f
|
1124 |
-
*/
|
1125 |
public function transform($a, $b, $c, $d, $e, $f)
|
1126 |
{
|
1127 |
$this->_pdf->concat($a, $b, $c, $d, $e, $f);
|
1128 |
}
|
1129 |
|
1130 |
-
|
1131 |
-
* @param array $points
|
1132 |
-
* @param array $color
|
1133 |
-
* @param null $width
|
1134 |
-
* @param null $style
|
1135 |
-
* @param bool $fill
|
1136 |
-
*/
|
1137 |
-
public function polygon($points, $color, $width = null, $style = null, $fill = false)
|
1138 |
{
|
1139 |
$this->_set_fill_color($color);
|
1140 |
$this->_set_stroke_color($color);
|
@@ -1163,16 +1037,7 @@ class PDFLib implements Canvas
|
|
1163 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
1164 |
}
|
1165 |
|
1166 |
-
|
1167 |
-
* @param float $x
|
1168 |
-
* @param float $y
|
1169 |
-
* @param float $r
|
1170 |
-
* @param array $color
|
1171 |
-
* @param null $width
|
1172 |
-
* @param null $style
|
1173 |
-
* @param bool $fill
|
1174 |
-
*/
|
1175 |
-
public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false)
|
1176 |
{
|
1177 |
$this->_set_fill_color($color);
|
1178 |
$this->_set_stroke_color($color);
|
@@ -1195,37 +1060,34 @@ class PDFLib implements Canvas
|
|
1195 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
1196 |
}
|
1197 |
|
1198 |
-
|
1199 |
-
* @param string $img_url
|
1200 |
-
* @param float $x
|
1201 |
-
* @param float $y
|
1202 |
-
* @param int $w
|
1203 |
-
* @param int $h
|
1204 |
-
* @param string $resolution
|
1205 |
-
*/
|
1206 |
-
public function image($img_url, $x, $y, $w, $h, $resolution = "normal")
|
1207 |
{
|
1208 |
$w = (int)$w;
|
1209 |
$h = (int)$h;
|
1210 |
|
1211 |
-
$img_type = Cache::detect_type($
|
|
|
|
|
|
|
|
|
|
|
1212 |
|
1213 |
-
if (!isset($this->_imgs[$
|
1214 |
if (strtolower($img_type) === "svg") {
|
1215 |
//FIXME: PDFLib loads SVG but returns error message "Function must not be called in 'page' scope"
|
1216 |
-
$image_load_response = $this->_pdf->load_graphics($img_type, $
|
1217 |
} else {
|
1218 |
-
$image_load_response = $this->_pdf->load_image($img_type, $
|
1219 |
}
|
1220 |
if ($image_load_response === 0) {
|
1221 |
//TODO: should do something with the error message
|
1222 |
$error = $this->_pdf->get_errmsg();
|
1223 |
return;
|
1224 |
}
|
1225 |
-
$this->_imgs[$
|
1226 |
}
|
1227 |
|
1228 |
-
$img = $this->_imgs[$
|
1229 |
|
1230 |
$y = $this->y($y) - $h;
|
1231 |
if (strtolower($img_type) === "svg") {
|
@@ -1235,19 +1097,12 @@ class PDFLib implements Canvas
|
|
1235 |
}
|
1236 |
}
|
1237 |
|
1238 |
-
/**
|
1239 |
-
* @param float $x
|
1240 |
-
* @param float $y
|
1241 |
-
* @param string $text
|
1242 |
-
* @param string $font
|
1243 |
-
* @param float $size
|
1244 |
-
* @param array $color
|
1245 |
-
* @param int $word_spacing
|
1246 |
-
* @param int $char_spacing
|
1247 |
-
* @param int $angle
|
1248 |
-
*/
|
1249 |
public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0, $char_spacing = 0, $angle = 0)
|
1250 |
{
|
|
|
|
|
|
|
|
|
1251 |
$fh = $this->_load_font($font);
|
1252 |
|
1253 |
$this->_pdf->setfont($fh, $size);
|
@@ -1264,9 +1119,6 @@ class PDFLib implements Canvas
|
|
1264 |
$this->_set_fill_opacity($this->_current_opacity, "Normal");
|
1265 |
}
|
1266 |
|
1267 |
-
/**
|
1268 |
-
* @param string $code
|
1269 |
-
*/
|
1270 |
public function javascript($code)
|
1271 |
{
|
1272 |
if (strlen($this->_dompdf->getOptions()->getPdflibLicense()) > 0) {
|
@@ -1274,25 +1126,11 @@ class PDFLib implements Canvas
|
|
1274 |
}
|
1275 |
}
|
1276 |
|
1277 |
-
/**
|
1278 |
-
* Add a named destination (similar to <a name="foo">...</a> in html)
|
1279 |
-
*
|
1280 |
-
* @param string $anchorname The name of the named destination
|
1281 |
-
*/
|
1282 |
public function add_named_dest($anchorname)
|
1283 |
{
|
1284 |
$this->_pdf->add_nameddest($anchorname, "");
|
1285 |
}
|
1286 |
|
1287 |
-
/**
|
1288 |
-
* Add a link to the pdf
|
1289 |
-
*
|
1290 |
-
* @param string $url The url to link to
|
1291 |
-
* @param float $x The x position of the link
|
1292 |
-
* @param float $y The y position of the link
|
1293 |
-
* @param float $width The width of the link
|
1294 |
-
* @param float $height The height of the link
|
1295 |
-
*/
|
1296 |
public function add_link($url, $x, $y, $width, $height)
|
1297 |
{
|
1298 |
$y = $this->y($y) - $height;
|
@@ -1304,29 +1142,21 @@ class PDFLib implements Canvas
|
|
1304 |
"contents={$url} destname=" . substr($url, 1) . " linewidth=0");
|
1305 |
}
|
1306 |
} else {
|
1307 |
-
|
1308 |
-
|
1309 |
-
|
1310 |
-
|
|
|
1311 |
}
|
1312 |
-
$url = Helpers::build_url($proto, $host, $path, $file);
|
1313 |
-
$url = '{' . rawurldecode($url) . '}';
|
1314 |
-
|
1315 |
-
$action = $this->_pdf->create_action("URI", "url=" . $url);
|
1316 |
-
$this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0");
|
1317 |
}
|
1318 |
}
|
1319 |
|
1320 |
-
|
1321 |
-
* @param string $text
|
1322 |
-
* @param string $font
|
1323 |
-
* @param float $size
|
1324 |
-
* @param float $word_spacing
|
1325 |
-
* @param float $letter_spacing
|
1326 |
-
* @return mixed
|
1327 |
-
*/
|
1328 |
-
public function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0)
|
1329 |
{
|
|
|
|
|
|
|
|
|
1330 |
$fh = $this->_load_font($font);
|
1331 |
|
1332 |
// Determine the additional width due to extra spacing
|
@@ -1341,13 +1171,12 @@ class PDFLib implements Canvas
|
|
1341 |
return $this->_pdf->stringwidth($text, $fh, $size) + $delta;
|
1342 |
}
|
1343 |
|
1344 |
-
/**
|
1345 |
-
* @param string $font
|
1346 |
-
* @param float $size
|
1347 |
-
* @return float
|
1348 |
-
*/
|
1349 |
public function get_font_height($font, $size)
|
1350 |
{
|
|
|
|
|
|
|
|
|
1351 |
$fh = $this->_load_font($font);
|
1352 |
|
1353 |
$this->_pdf->setfont($fh, $size);
|
@@ -1361,11 +1190,6 @@ class PDFLib implements Canvas
|
|
1361 |
return (abs($asc) + abs($desc)) * $ratio;
|
1362 |
}
|
1363 |
|
1364 |
-
/**
|
1365 |
-
* @param string $font
|
1366 |
-
* @param float $size
|
1367 |
-
* @return float
|
1368 |
-
*/
|
1369 |
public function get_font_baseline($font, $size)
|
1370 |
{
|
1371 |
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
|
@@ -1374,60 +1198,55 @@ class PDFLib implements Canvas
|
|
1374 |
}
|
1375 |
|
1376 |
/**
|
1377 |
-
*
|
1378 |
*
|
1379 |
-
* The
|
1380 |
-
*
|
|
|
|
|
|
|
1381 |
*
|
1382 |
-
*
|
|
|
1383 |
*
|
1384 |
-
* @param
|
1385 |
-
* @param float $y
|
1386 |
-
* @param string $text the text to write
|
1387 |
-
* @param string $font the font file to use
|
1388 |
-
* @param float $size the font size, in points
|
1389 |
-
* @param array $color
|
1390 |
-
* @param float $word_space word spacing adjustment
|
1391 |
-
* @param float $char_space char spacing adjustment
|
1392 |
-
* @param float $angle angle to write the text at, measured CW starting from the x-axis
|
1393 |
*/
|
1394 |
-
public function
|
1395 |
{
|
1396 |
-
$
|
1397 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1398 |
}
|
1399 |
|
1400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1401 |
|
1402 |
-
|
1403 |
-
* Processes a callback or script on every page
|
1404 |
-
*
|
1405 |
-
* The callback function receives the four parameters `$pageNumber`,
|
1406 |
-
* `$pageCount`, `$pdf`, and `$fontMetrics`, in that order. If a script is
|
1407 |
-
* passed as string, the variables `$PAGE_NUM`, `$PAGE_COUNT`, `$pdf`, and
|
1408 |
-
* `$fontMetrics` are available instead.
|
1409 |
-
*
|
1410 |
-
* This function can be used to add page numbers to all pages after the
|
1411 |
-
* first one, for example.
|
1412 |
-
*
|
1413 |
-
* @param callable|string $code The callback function or PHP script to process on every page
|
1414 |
-
*/
|
1415 |
-
public function page_script($code)
|
1416 |
{
|
1417 |
-
|
1418 |
-
$this->
|
1419 |
-
|
1420 |
-
"callback" => $code
|
1421 |
-
];
|
1422 |
-
} else {
|
1423 |
-
$_t = "script";
|
1424 |
-
$this->_page_text[] = compact("_t", "code");
|
1425 |
-
}
|
1426 |
}
|
1427 |
|
1428 |
-
/**
|
1429 |
-
*
|
1430 |
-
*/
|
1431 |
public function new_page()
|
1432 |
{
|
1433 |
// Add objects to the current page
|
@@ -1438,49 +1257,15 @@ class PDFLib implements Canvas
|
|
1438 |
$this->_page_number = ++$this->_page_count;
|
1439 |
}
|
1440 |
|
1441 |
-
|
1442 |
-
* Add text to each page after rendering is complete
|
1443 |
-
*/
|
1444 |
-
protected function _add_page_text()
|
1445 |
{
|
1446 |
-
if (count($this->_page_text) === 0) {
|
1447 |
-
return;
|
1448 |
-
}
|
1449 |
-
|
1450 |
-
$eval = null;
|
1451 |
$this->_pdf->suspend_page("");
|
1452 |
|
1453 |
for ($p = 1; $p <= $this->_page_count; $p++) {
|
1454 |
$this->_pdf->resume_page("pagenumber=$p");
|
1455 |
|
1456 |
-
|
1457 |
-
|
1458 |
-
|
1459 |
-
switch ($_t) {
|
1460 |
-
case "text":
|
1461 |
-
$text = str_replace(["{PAGE_NUM}", "{PAGE_COUNT}"],
|
1462 |
-
[$p, $this->_page_count], $text);
|
1463 |
-
$this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
|
1464 |
-
break;
|
1465 |
-
|
1466 |
-
case "callback":
|
1467 |
-
$fontMetrics = $this->get_dompdf()->getFontMetrics();
|
1468 |
-
$callback($p, $this->_page_count, $this, $fontMetrics);
|
1469 |
-
break;
|
1470 |
-
|
1471 |
-
case "script":
|
1472 |
-
if (!$eval) {
|
1473 |
-
$eval = new PHPEvaluator($this);
|
1474 |
-
}
|
1475 |
-
$eval->evaluate($code, ["PAGE_NUM" => $p, "PAGE_COUNT" => $this->_page_count]);
|
1476 |
-
break;
|
1477 |
-
|
1478 |
-
case "line":
|
1479 |
-
$this->line($x1, $y1, $x2, $y2, $color, $width, $style);
|
1480 |
-
break;
|
1481 |
-
|
1482 |
-
}
|
1483 |
-
}
|
1484 |
|
1485 |
$this->_pdf->suspend_page("");
|
1486 |
}
|
@@ -1489,10 +1274,6 @@ class PDFLib implements Canvas
|
|
1489 |
}
|
1490 |
|
1491 |
/**
|
1492 |
-
* Streams the PDF to the client.
|
1493 |
-
*
|
1494 |
-
* @param string $filename The filename to present to the client.
|
1495 |
-
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
|
1496 |
* @throws Exception
|
1497 |
*/
|
1498 |
public function stream($filename = "document.pdf", $options = [])
|
@@ -1508,8 +1289,6 @@ class PDFLib implements Canvas
|
|
1508 |
$options["Attachment"] = true;
|
1509 |
}
|
1510 |
|
1511 |
-
$this->_add_page_text();
|
1512 |
-
|
1513 |
if ($options["compress"]) {
|
1514 |
$this->setPDFLibValue("compress", 6);
|
1515 |
} else {
|
@@ -1564,20 +1343,12 @@ class PDFLib implements Canvas
|
|
1564 |
flush();
|
1565 |
}
|
1566 |
|
1567 |
-
/**
|
1568 |
-
* Returns the PDF as a string.
|
1569 |
-
*
|
1570 |
-
* @param array $options Associative array: 'compress' => 1 or 0 (default 1).
|
1571 |
-
* @return string
|
1572 |
-
*/
|
1573 |
public function output($options = [])
|
1574 |
{
|
1575 |
if (!isset($options["compress"])) {
|
1576 |
$options["compress"] = true;
|
1577 |
}
|
1578 |
|
1579 |
-
$this->_add_page_text();
|
1580 |
-
|
1581 |
if ($options["compress"]) {
|
1582 |
$this->setPDFLibValue("compress", 6);
|
1583 |
} else {
|
11 |
|
12 |
use Dompdf\Canvas;
|
13 |
use Dompdf\Dompdf;
|
|
|
14 |
use Dompdf\Exception;
|
15 |
+
use Dompdf\FontMetrics;
|
16 |
+
use Dompdf\Helpers;
|
17 |
use Dompdf\Image\Cache;
|
|
|
18 |
|
19 |
/**
|
20 |
* PDF rendering interface
|
37 |
/**
|
38 |
* Dimensions of paper sizes in points
|
39 |
*
|
40 |
+
* @var array
|
41 |
*/
|
42 |
public static $PAPER_SIZES = []; // Set to Dompdf\Adapter\CPDF::$PAPER_SIZES below.
|
43 |
|
130 |
/**
|
131 |
* The current opacity level
|
132 |
*
|
133 |
+
* @var float|null
|
134 |
*/
|
135 |
protected $_current_opacity;
|
136 |
|
184 |
protected $_page_count;
|
185 |
|
186 |
/**
|
187 |
+
* Array of pages for accessing after rendering is initially complete
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
*
|
189 |
* @var array
|
190 |
*/
|
191 |
protected $_pages;
|
192 |
|
193 |
+
public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
{
|
195 |
if (is_array($paper)) {
|
196 |
+
$size = array_map("floatval", $paper);
|
|
|
|
|
197 |
} else {
|
198 |
+
$paper = strtolower($paper);
|
199 |
+
$size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"];
|
200 |
}
|
201 |
|
202 |
+
if (strtolower($orientation) === "landscape") {
|
203 |
+
[$size[2], $size[3]] = [$size[3], $size[2]];
|
204 |
}
|
205 |
|
206 |
$this->_width = $size[2] - $size[0];
|
219 |
$this->setPDFLibParameter("license", $license);
|
220 |
}
|
221 |
|
222 |
+
if ($this->getPDFLibMajorVersion() < 10) {
|
223 |
+
$this->setPDFLibParameter("textformat", "utf8");
|
224 |
+
}
|
225 |
if ($this->getPDFLibMajorVersion() >= 7) {
|
226 |
$this->setPDFLibParameter("errorpolicy", "return");
|
227 |
// $this->_pdf->set_option('logging={filename=' . \APP_PATH . '/logs/pdflib.log classes={api=1 warning=2}}');
|
257 |
$this->_pdf->begin_page_ext($this->_width, $this->_height, "");
|
258 |
|
259 |
$this->_page_number = $this->_page_count = 1;
|
|
|
260 |
|
261 |
$this->_imgs = [];
|
262 |
$this->_fonts = [];
|
263 |
$this->_objs = [];
|
264 |
}
|
265 |
|
|
|
|
|
|
|
266 |
function get_dompdf()
|
267 |
{
|
268 |
return $this->_dompdf;
|
296 |
return $this->_pdf;
|
297 |
}
|
298 |
|
299 |
+
public function add_info(string $label, string $value): void
|
|
|
|
|
|
|
|
|
|
|
|
|
300 |
{
|
301 |
$this->_pdf->set_info($label, $value);
|
302 |
}
|
440 |
}
|
441 |
}
|
442 |
|
|
|
|
|
|
|
443 |
public function get_width()
|
444 |
{
|
445 |
return $this->_width;
|
446 |
}
|
447 |
|
|
|
|
|
|
|
448 |
public function get_height()
|
449 |
{
|
450 |
return $this->_height;
|
451 |
}
|
452 |
|
|
|
|
|
|
|
453 |
public function get_page_number()
|
454 |
{
|
455 |
return $this->_page_number;
|
456 |
}
|
457 |
|
|
|
|
|
|
|
458 |
public function get_page_count()
|
459 |
{
|
460 |
return $this->_page_count;
|
468 |
$this->_page_number = (int)$num;
|
469 |
}
|
470 |
|
|
|
|
|
|
|
471 |
public function set_page_count($count)
|
472 |
{
|
473 |
$this->_page_count = (int)$count;
|
477 |
* Sets the line style
|
478 |
*
|
479 |
* @param float $width
|
480 |
+
* @param string $cap
|
481 |
* @param string $join
|
482 |
* @param array $dash
|
|
|
|
|
483 |
*/
|
484 |
protected function _set_line_style($width, $cap, $join, $dash)
|
485 |
{
|
486 |
if (!is_array($dash)) {
|
487 |
+
$dash = [];
|
488 |
+
}
|
489 |
+
|
490 |
+
// Work around PDFLib limitation with 0 dash length:
|
491 |
+
// Value 0 for option 'dasharray' is too small (minimum 1.5e-05)
|
492 |
+
foreach ($dash as &$d) {
|
493 |
+
if ($d == 0) {
|
494 |
+
$d = 1.5e-5;
|
495 |
+
}
|
496 |
}
|
497 |
|
498 |
+
if (count($dash) === 1) {
|
499 |
$dash[] = $dash[0];
|
500 |
}
|
501 |
|
651 |
/**
|
652 |
* Sets the fill opacity
|
653 |
*
|
654 |
+
* @param float $opacity
|
655 |
+
* @param string $mode
|
656 |
*/
|
657 |
public function _set_fill_opacity($opacity, $mode = "Normal")
|
658 |
{
|
659 |
+
if ($mode === "Normal" && isset($opacity)) {
|
660 |
$this->_set_gstate("opacityfill=$opacity");
|
661 |
}
|
662 |
}
|
664 |
/**
|
665 |
* Sets the stroke opacity
|
666 |
*
|
667 |
+
* @param float $opacity
|
668 |
+
* @param string $mode
|
669 |
*/
|
670 |
public function _set_stroke_opacity($opacity, $mode = "Normal")
|
671 |
{
|
672 |
+
if ($mode === "Normal" && isset($opacity)) {
|
673 |
$this->_set_gstate("opacitystroke=$opacity");
|
674 |
}
|
675 |
}
|
676 |
|
677 |
+
public function set_opacity(float $opacity, string $mode = "Normal"): void
|
|
|
|
|
|
|
|
|
|
|
|
|
678 |
{
|
679 |
+
if ($mode === "Normal") {
|
680 |
$this->_set_gstate("opacityfill=$opacity opacitystroke=$opacity");
|
681 |
$this->_current_opacity = $opacity;
|
682 |
}
|
835 |
return $this->_height - $y;
|
836 |
}
|
837 |
|
838 |
+
public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
839 |
{
|
840 |
+
$this->_set_line_style($width, $cap, "", $style);
|
841 |
$this->_set_stroke_color($color);
|
842 |
|
843 |
$y1 = $this->y($y1);
|
850 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
851 |
}
|
852 |
|
853 |
+
public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
854 |
{
|
855 |
+
$this->_set_line_style($width, $cap, "", $style);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
856 |
$this->_set_stroke_color($color);
|
857 |
|
858 |
+
$y = $this->y($y);
|
859 |
|
860 |
+
$this->_pdf->arc($x, $y, $r1, $astart, $aend);
|
861 |
$this->_pdf->stroke();
|
862 |
|
863 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
864 |
}
|
865 |
|
866 |
+
public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
867 |
{
|
868 |
$this->_set_stroke_color($color);
|
869 |
+
$this->_set_line_style($width, $cap, "", $style);
|
870 |
|
871 |
$y1 = $this->y($y1) - $h;
|
872 |
|
876 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
877 |
}
|
878 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
879 |
public function filled_rectangle($x1, $y1, $w, $h, $color)
|
880 |
{
|
881 |
$this->_set_fill_color($color);
|
888 |
$this->_set_fill_opacity($this->_current_opacity, "Normal");
|
889 |
}
|
890 |
|
|
|
|
|
|
|
|
|
|
|
|
|
891 |
public function clipping_rectangle($x1, $y1, $w, $h)
|
892 |
{
|
893 |
$this->_pdf->save();
|
898 |
$this->_pdf->clip();
|
899 |
}
|
900 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
901 |
public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
902 |
{
|
903 |
if ($this->getPDFLibMajorVersion() < 9) {
|
917 |
// line: left edge, bottom end
|
918 |
$path = $this->_pdf->add_path_point($path, 0, 0 + $rBL, "line", "");
|
919 |
// curve: bottom-left corner
|
920 |
+
if ($rBL > 0) {
|
921 |
+
$path = $this->_pdf->add_path_point($path, 0 + $rBL, 0, "elliptical", "radius=$rBL clockwise=false");
|
922 |
+
}
|
923 |
// line: bottom edge, left end
|
924 |
$path = $this->_pdf->add_path_point($path, 0 - $rBR + $w, 0, "line", "");
|
925 |
// curve: bottom-right corner
|
926 |
+
if ($rBR > 0) {
|
927 |
+
$path = $this->_pdf->add_path_point($path, 0 + $w, 0 + $rBR, "elliptical", "radius=$rBR clockwise=false");
|
928 |
+
}
|
929 |
// line: right edge, top end
|
930 |
$path = $this->_pdf->add_path_point($path, 0 + $w, 0 - $rTR + $h, "line", "");
|
931 |
// curve: top-right corner
|
932 |
+
if ($rTR > 0) {
|
933 |
+
$path = $this->_pdf->add_path_point($path, 0 - $rTR + $w, 0 + $h, "elliptical", "radius=$rTR clockwise=false");
|
934 |
+
}
|
935 |
// line: top edge, left end
|
936 |
$path = $this->_pdf->add_path_point($path, 0 + $rTL, 0 + $h, "line", "");
|
937 |
// curve: top-left corner
|
938 |
+
if ($rTL > 0) {
|
939 |
+
$path = $this->_pdf->add_path_point($path, 0, 0 - $rTL + $h, "elliptical", "radius=$rTL clockwise=false");
|
940 |
+
}
|
941 |
$this->_pdf->draw_path($path, $x1, $this->_height-$y1-$h, "clip=true");
|
942 |
}
|
943 |
|
944 |
+
public function clipping_polygon(array $points): void
|
945 |
+
{
|
946 |
+
$this->_pdf->save();
|
947 |
+
|
948 |
+
$y = $this->y(array_pop($points));
|
949 |
+
$x = array_pop($points);
|
950 |
+
$this->_pdf->moveto($x, $y);
|
951 |
+
|
952 |
+
while (count($points) > 1) {
|
953 |
+
$y = $this->y(array_pop($points));
|
954 |
+
$x = array_pop($points);
|
955 |
+
$this->_pdf->lineto($x, $y);
|
956 |
+
}
|
957 |
+
|
958 |
+
$this->_pdf->closepath();
|
959 |
+
$this->_pdf->clip();
|
960 |
+
}
|
961 |
+
|
962 |
public function clipping_end()
|
963 |
{
|
964 |
$this->_pdf->restore();
|
965 |
}
|
966 |
|
|
|
|
|
|
|
967 |
public function save()
|
968 |
{
|
969 |
$this->_pdf->save();
|
974 |
$this->_pdf->restore();
|
975 |
}
|
976 |
|
|
|
|
|
|
|
|
|
|
|
977 |
public function rotate($angle, $x, $y)
|
978 |
{
|
979 |
$pdf = $this->_pdf;
|
982 |
$pdf->translate(-$x, -$this->_height + $y);
|
983 |
}
|
984 |
|
|
|
|
|
|
|
|
|
|
|
|
|
985 |
public function skew($angle_x, $angle_y, $x, $y)
|
986 |
{
|
987 |
$pdf = $this->_pdf;
|
990 |
$pdf->translate(-$x, -$this->_height + $y);
|
991 |
}
|
992 |
|
|
|
|
|
|
|
|
|
|
|
|
|
993 |
public function scale($s_x, $s_y, $x, $y)
|
994 |
{
|
995 |
$pdf = $this->_pdf;
|
998 |
$pdf->translate(-$x, -$this->_height + $y);
|
999 |
}
|
1000 |
|
|
|
|
|
|
|
|
|
1001 |
public function translate($t_x, $t_y)
|
1002 |
{
|
1003 |
$this->_pdf->translate($t_x, -$t_y);
|
1004 |
}
|
1005 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1006 |
public function transform($a, $b, $c, $d, $e, $f)
|
1007 |
{
|
1008 |
$this->_pdf->concat($a, $b, $c, $d, $e, $f);
|
1009 |
}
|
1010 |
|
1011 |
+
public function polygon($points, $color, $width = null, $style = [], $fill = false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1012 |
{
|
1013 |
$this->_set_fill_color($color);
|
1014 |
$this->_set_stroke_color($color);
|
1037 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
1038 |
}
|
1039 |
|
1040 |
+
public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1041 |
{
|
1042 |
$this->_set_fill_color($color);
|
1043 |
$this->_set_stroke_color($color);
|
1060 |
$this->_set_stroke_opacity($this->_current_opacity, "Normal");
|
1061 |
}
|
1062 |
|
1063 |
+
public function image($img, $x, $y, $w, $h, $resolution = "normal")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1064 |
{
|
1065 |
$w = (int)$w;
|
1066 |
$h = (int)$h;
|
1067 |
|
1068 |
+
$img_type = Cache::detect_type($img, $this->get_dompdf()->getHttpContext());
|
1069 |
+
|
1070 |
+
// Strip file:// prefix
|
1071 |
+
if (substr($img, 0, 7) === "file://") {
|
1072 |
+
$img = substr($img, 7);
|
1073 |
+
}
|
1074 |
|
1075 |
+
if (!isset($this->_imgs[$img])) {
|
1076 |
if (strtolower($img_type) === "svg") {
|
1077 |
//FIXME: PDFLib loads SVG but returns error message "Function must not be called in 'page' scope"
|
1078 |
+
$image_load_response = $this->_pdf->load_graphics($img_type, $img, "");
|
1079 |
} else {
|
1080 |
+
$image_load_response = $this->_pdf->load_image($img_type, $img, "");
|
1081 |
}
|
1082 |
if ($image_load_response === 0) {
|
1083 |
//TODO: should do something with the error message
|
1084 |
$error = $this->_pdf->get_errmsg();
|
1085 |
return;
|
1086 |
}
|
1087 |
+
$this->_imgs[$img] = $image_load_response;
|
1088 |
}
|
1089 |
|
1090 |
+
$img = $this->_imgs[$img];
|
1091 |
|
1092 |
$y = $this->y($y) - $h;
|
1093 |
if (strtolower($img_type) === "svg") {
|
1097 |
}
|
1098 |
}
|
1099 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1100 |
public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0, $char_spacing = 0, $angle = 0)
|
1101 |
{
|
1102 |
+
if ($size == 0) {
|
1103 |
+
return;
|
1104 |
+
}
|
1105 |
+
|
1106 |
$fh = $this->_load_font($font);
|
1107 |
|
1108 |
$this->_pdf->setfont($fh, $size);
|
1119 |
$this->_set_fill_opacity($this->_current_opacity, "Normal");
|
1120 |
}
|
1121 |
|
|
|
|
|
|
|
1122 |
public function javascript($code)
|
1123 |
{
|
1124 |
if (strlen($this->_dompdf->getOptions()->getPdflibLicense()) > 0) {
|
1126 |
}
|
1127 |
}
|
1128 |
|
|
|
|
|
|
|
|
|
|
|
1129 |
public function add_named_dest($anchorname)
|
1130 |
{
|
1131 |
$this->_pdf->add_nameddest($anchorname, "");
|
1132 |
}
|
1133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1134 |
public function add_link($url, $x, $y, $width, $height)
|
1135 |
{
|
1136 |
$y = $this->y($y) - $height;
|
1142 |
"contents={$url} destname=" . substr($url, 1) . " linewidth=0");
|
1143 |
}
|
1144 |
} else {
|
1145 |
+
//TODO: PDFLib::create_action does not permit non-HTTP links for URI actions
|
1146 |
+
$action = $this->_pdf->create_action("URI", "url={{$url}}");
|
1147 |
+
// add the annotation only if the action was created
|
1148 |
+
if ($action !== 0) {
|
1149 |
+
$this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={{$url}} action={activate=$action} linewidth=0");
|
1150 |
}
|
|
|
|
|
|
|
|
|
|
|
1151 |
}
|
1152 |
}
|
1153 |
|
1154 |
+
public function get_text_width($text, $font, $size, $word_spacing = 0.0, $letter_spacing = 0.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1155 |
{
|
1156 |
+
if ($size == 0) {
|
1157 |
+
return 0.0;
|
1158 |
+
}
|
1159 |
+
|
1160 |
$fh = $this->_load_font($font);
|
1161 |
|
1162 |
// Determine the additional width due to extra spacing
|
1171 |
return $this->_pdf->stringwidth($text, $fh, $size) + $delta;
|
1172 |
}
|
1173 |
|
|
|
|
|
|
|
|
|
|
|
1174 |
public function get_font_height($font, $size)
|
1175 |
{
|
1176 |
+
if ($size == 0) {
|
1177 |
+
return 0.0;
|
1178 |
+
}
|
1179 |
+
|
1180 |
$fh = $this->_load_font($font);
|
1181 |
|
1182 |
$this->_pdf->setfont($fh, $size);
|
1190 |
return (abs($asc) + abs($desc)) * $ratio;
|
1191 |
}
|
1192 |
|
|
|
|
|
|
|
|
|
|
|
1193 |
public function get_font_baseline($font, $size)
|
1194 |
{
|
1195 |
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
|
1198 |
}
|
1199 |
|
1200 |
/**
|
1201 |
+
* Processes a callback or script on every page.
|
1202 |
*
|
1203 |
+
* The callback function receives the four parameters `int $pageNumber`,
|
1204 |
+
* `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
|
1205 |
+
* that order. If a script is passed as string, the variables `$PAGE_NUM`,
|
1206 |
+
* `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing
|
1207 |
+
* a script as string is deprecated and will be removed in a future version.
|
1208 |
*
|
1209 |
+
* This function can be used to add page numbers to all pages after the
|
1210 |
+
* first one, for example.
|
1211 |
*
|
1212 |
+
* @param callable|string $callback The callback function or PHP script to process on every page
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1213 |
*/
|
1214 |
+
public function page_script($callback): void
|
1215 |
{
|
1216 |
+
if (is_string($callback)) {
|
1217 |
+
$this->processPageScript(function (
|
1218 |
+
int $PAGE_NUM,
|
1219 |
+
int $PAGE_COUNT,
|
1220 |
+
self $pdf,
|
1221 |
+
FontMetrics $fontMetrics
|
1222 |
+
) use ($callback) {
|
1223 |
+
eval($callback);
|
1224 |
+
});
|
1225 |
+
return;
|
1226 |
+
}
|
1227 |
+
|
1228 |
+
$this->processPageScript($callback);
|
1229 |
}
|
1230 |
|
1231 |
+
public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
|
1232 |
+
{
|
1233 |
+
$this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) {
|
1234 |
+
$text = str_replace(
|
1235 |
+
["{PAGE_NUM}", "{PAGE_COUNT}"],
|
1236 |
+
[$pageNumber, $pageCount],
|
1237 |
+
$text
|
1238 |
+
);
|
1239 |
+
$this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
|
1240 |
+
});
|
1241 |
+
}
|
1242 |
|
1243 |
+
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1244 |
{
|
1245 |
+
$this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) {
|
1246 |
+
$this->line($x1, $y1, $x2, $y2, $color, $width, $style);
|
1247 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
1248 |
}
|
1249 |
|
|
|
|
|
|
|
1250 |
public function new_page()
|
1251 |
{
|
1252 |
// Add objects to the current page
|
1257 |
$this->_page_number = ++$this->_page_count;
|
1258 |
}
|
1259 |
|
1260 |
+
protected function processPageScript(callable $callback): void
|
|
|
|
|
|
|
1261 |
{
|
|
|
|
|
|
|
|
|
|
|
1262 |
$this->_pdf->suspend_page("");
|
1263 |
|
1264 |
for ($p = 1; $p <= $this->_page_count; $p++) {
|
1265 |
$this->_pdf->resume_page("pagenumber=$p");
|
1266 |
|
1267 |
+
$fontMetrics = $this->_dompdf->getFontMetrics();
|
1268 |
+
$callback($p, $this->_page_count, $this, $fontMetrics);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1269 |
|
1270 |
$this->_pdf->suspend_page("");
|
1271 |
}
|
1274 |
}
|
1275 |
|
1276 |
/**
|
|
|
|
|
|
|
|
|
1277 |
* @throws Exception
|
1278 |
*/
|
1279 |
public function stream($filename = "document.pdf", $options = [])
|
1289 |
$options["Attachment"] = true;
|
1290 |
}
|
1291 |
|
|
|
|
|
1292 |
if ($options["compress"]) {
|
1293 |
$this->setPDFLibValue("compress", 6);
|
1294 |
} else {
|
1343 |
flush();
|
1344 |
}
|
1345 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1346 |
public function output($options = [])
|
1347 |
{
|
1348 |
if (!isset($options["compress"])) {
|
1349 |
$options["compress"] = true;
|
1350 |
}
|
1351 |
|
|
|
|
|
1352 |
if ($options["compress"]) {
|
1353 |
$this->setPDFLibValue("compress", 6);
|
1354 |
} else {
|
vendor/dompdf/dompdf/src/Canvas.php
CHANGED
@@ -24,7 +24,13 @@ namespace Dompdf;
|
|
24 |
*/
|
25 |
interface Canvas
|
26 |
{
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
/**
|
30 |
* @return Dompdf
|
@@ -39,7 +45,7 @@ interface Canvas
|
|
39 |
function get_page_number();
|
40 |
|
41 |
/**
|
42 |
-
* Returns the total number of pages
|
43 |
*
|
44 |
* @return int
|
45 |
*/
|
@@ -55,47 +61,68 @@ interface Canvas
|
|
55 |
/**
|
56 |
* Draws a line from x1,y1 to x2,y2
|
57 |
*
|
58 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
59 |
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
60 |
-
* $style
|
61 |
*
|
62 |
-
* @param float
|
63 |
-
* @param float
|
64 |
-
* @param float
|
65 |
-
* @param float
|
66 |
-
* @param array
|
67 |
-
*
|
68 |
-
* @param
|
|
|
|
|
69 |
*/
|
70 |
-
function line($x1, $y1, $x2, $y2, $color, $width, $style =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
/**
|
73 |
* Draws a rectangle at x1,y1 with width w and height h
|
74 |
*
|
75 |
-
* See {@link
|
76 |
-
*
|
77 |
-
* parameter (aka dash)
|
78 |
*
|
79 |
-
* @param float
|
80 |
-
* @param float
|
81 |
-
* @param float
|
82 |
-
* @param float
|
83 |
-
* @param array
|
84 |
-
*
|
85 |
-
* @param
|
|
|
|
|
86 |
*/
|
87 |
-
function rectangle($x1, $y1, $w, $h, $color, $width, $style =
|
88 |
|
89 |
/**
|
90 |
* Draws a filled rectangle at x1,y1 with width w and height h
|
91 |
*
|
92 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
93 |
-
*
|
94 |
* @param float $x1
|
95 |
* @param float $y1
|
96 |
* @param float $w
|
97 |
* @param float $h
|
98 |
-
* @param array $color
|
|
|
99 |
*/
|
100 |
function filled_rectangle($x1, $y1, $w, $h, $color);
|
101 |
|
@@ -120,65 +147,67 @@ interface Canvas
|
|
120 |
* @param float $tr
|
121 |
* @param float $br
|
122 |
* @param float $bl
|
123 |
-
*
|
124 |
-
* @return
|
125 |
*/
|
126 |
function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
|
127 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
/**
|
129 |
* Ends the last clipping shape
|
130 |
*/
|
131 |
function clipping_end();
|
132 |
|
133 |
/**
|
134 |
-
* Processes a callback on every page
|
135 |
*
|
136 |
-
* The callback function receives the four parameters
|
137 |
-
*
|
|
|
138 |
*
|
139 |
* This function can be used to add page numbers to all pages after the
|
140 |
* first one, for example.
|
141 |
*
|
142 |
* @param callable $callback The callback function to process on every page
|
143 |
-
* @todo Enable with next major release
|
144 |
*/
|
145 |
-
|
146 |
|
147 |
/**
|
148 |
-
* Writes text at the specified x and y coordinates on every page
|
149 |
*
|
150 |
* The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
|
151 |
* with their current values.
|
152 |
*
|
153 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
154 |
-
*
|
155 |
* @param float $x
|
156 |
* @param float $y
|
157 |
-
* @param string $text
|
158 |
-
* @param string $font
|
159 |
-
* @param float $size
|
160 |
-
* @param array $color
|
161 |
-
*
|
162 |
-
* @param float $
|
163 |
-
* @param float $
|
|
|
164 |
*/
|
165 |
public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
|
166 |
|
167 |
/**
|
168 |
-
*
|
169 |
-
*
|
170 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
171 |
*
|
172 |
* @param float $x1
|
173 |
* @param float $y1
|
174 |
* @param float $x2
|
175 |
* @param float $y2
|
176 |
-
* @param array $color
|
|
|
177 |
* @param float $width
|
178 |
-
* @param array $style
|
179 |
-
* @todo Enable with next major release
|
180 |
*/
|
181 |
-
|
182 |
|
183 |
/**
|
184 |
* Save current state
|
@@ -204,8 +233,8 @@ interface Canvas
|
|
204 |
*
|
205 |
* @param float $angle_x
|
206 |
* @param float $angle_y
|
207 |
-
* @param float $x
|
208 |
-
* @param float $y
|
209 |
*/
|
210 |
function skew($angle_x, $angle_y, $x, $y);
|
211 |
|
@@ -230,13 +259,12 @@ interface Canvas
|
|
230 |
/**
|
231 |
* Transform
|
232 |
*
|
233 |
-
* @param $a
|
234 |
-
* @param $b
|
235 |
-
* @param $c
|
236 |
-
* @param $d
|
237 |
-
* @param $e
|
238 |
-
* @param $f
|
239 |
-
* @return
|
240 |
*/
|
241 |
function transform($a, $b, $c, $d, $e, $f);
|
242 |
|
@@ -245,43 +273,43 @@ interface Canvas
|
|
245 |
*
|
246 |
* The polygon is formed by joining all the points stored in the $points
|
247 |
* array. $points has the following structure:
|
248 |
-
*
|
249 |
* array(0 => x1,
|
250 |
* 1 => y1,
|
251 |
* 2 => x2,
|
252 |
* 3 => y2,
|
253 |
* ...
|
254 |
* );
|
255 |
-
*
|
256 |
*
|
257 |
-
* See {@link
|
258 |
-
*
|
259 |
-
* parameter (aka dash)
|
260 |
*
|
261 |
* @param array $points
|
262 |
-
* @param array $color
|
|
|
263 |
* @param float $width
|
264 |
* @param array $style
|
265 |
-
* @param bool
|
266 |
*/
|
267 |
-
function polygon($points, $color, $width = null, $style =
|
268 |
|
269 |
/**
|
270 |
* Draws a circle at $x,$y with radius $r
|
271 |
*
|
272 |
-
* See {@link
|
273 |
-
*
|
274 |
-
* parameter (aka dash)
|
275 |
*
|
276 |
* @param float $x
|
277 |
* @param float $y
|
278 |
* @param float $r
|
279 |
-
* @param array $color
|
|
|
280 |
* @param float $width
|
281 |
* @param array $style
|
282 |
-
* @param bool
|
283 |
*/
|
284 |
-
function circle($x, $y, $r, $color, $width = null, $style =
|
285 |
|
286 |
/**
|
287 |
* Add an image to the pdf.
|
@@ -289,44 +317,28 @@ interface Canvas
|
|
289 |
* The image is placed at the specified x and y coordinates with the
|
290 |
* given width and height.
|
291 |
*
|
292 |
-
* @param string $
|
293 |
-
* @param float
|
294 |
-
* @param float
|
295 |
-
* @param
|
296 |
-
* @param
|
297 |
* @param string $resolution The resolution of the image
|
298 |
*/
|
299 |
-
function image($
|
300 |
-
|
301 |
-
/**
|
302 |
-
* Add an arc to the PDF
|
303 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
304 |
-
*
|
305 |
-
* @param float $x X coordinate of the arc
|
306 |
-
* @param float $y Y coordinate of the arc
|
307 |
-
* @param float $r1 Radius 1
|
308 |
-
* @param float $r2 Radius 2
|
309 |
-
* @param float $astart Start angle in degrees
|
310 |
-
* @param float $aend End angle in degrees
|
311 |
-
* @param array $color Color
|
312 |
-
* @param float $width
|
313 |
-
* @param array $style
|
314 |
-
*/
|
315 |
-
function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = []);
|
316 |
|
317 |
/**
|
318 |
* Writes text at the specified x and y coordinates
|
319 |
-
* See {@link Style::munge_color()} for the format of the color array.
|
320 |
*
|
321 |
-
* @param float
|
322 |
-
* @param float
|
323 |
-
* @param string $text
|
324 |
-
* @param string $font
|
325 |
-
* @param float
|
326 |
-
* @param array
|
327 |
-
*
|
328 |
-
* @param float
|
329 |
-
* @param float
|
|
|
330 |
*/
|
331 |
function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
|
332 |
|
@@ -340,30 +352,30 @@ interface Canvas
|
|
340 |
/**
|
341 |
* Add a link to the pdf
|
342 |
*
|
343 |
-
* @param string $url
|
344 |
-
* @param float
|
345 |
-
* @param float
|
346 |
-
* @param float
|
347 |
-
* @param float
|
348 |
*/
|
349 |
function add_link($url, $x, $y, $width, $height);
|
350 |
|
351 |
/**
|
352 |
-
* Add meta information to the
|
353 |
*
|
354 |
-
* @param string $
|
355 |
* @param string $value The text to set
|
356 |
*/
|
357 |
-
function add_info($
|
358 |
|
359 |
/**
|
360 |
* Calculates text size, in points
|
361 |
*
|
362 |
-
* @param string $text
|
363 |
-
* @param string $font
|
364 |
-
* @param float
|
365 |
-
* @param float
|
366 |
-
* @param float
|
367 |
*
|
368 |
* @return float
|
369 |
*/
|
@@ -372,55 +384,54 @@ interface Canvas
|
|
372 |
/**
|
373 |
* Calculates font height, in points
|
374 |
*
|
375 |
-
* @param string $font
|
376 |
-
* @param float
|
377 |
*
|
378 |
* @return float
|
379 |
*/
|
380 |
function get_font_height($font, $size);
|
381 |
|
382 |
/**
|
383 |
-
*
|
384 |
*
|
385 |
-
* @param string $font
|
386 |
-
* @param float
|
387 |
*
|
388 |
* @return float
|
389 |
*/
|
390 |
-
function
|
391 |
|
392 |
/**
|
393 |
-
*
|
|
|
|
|
|
|
394 |
*
|
395 |
* @return float
|
396 |
*/
|
397 |
-
function
|
398 |
-
|
399 |
|
400 |
/**
|
401 |
-
*
|
402 |
*
|
403 |
* @return float
|
404 |
*/
|
405 |
-
function
|
406 |
|
407 |
/**
|
408 |
-
* Returns the
|
409 |
-
*
|
410 |
-
* @param string $font
|
411 |
-
* @param float $size
|
412 |
*
|
413 |
* @return float
|
414 |
*/
|
415 |
-
|
416 |
|
417 |
/**
|
418 |
* Sets the opacity
|
419 |
*
|
420 |
-
* @param float
|
421 |
* @param string $mode
|
422 |
*/
|
423 |
-
function set_opacity($opacity, $mode = "Normal");
|
424 |
|
425 |
/**
|
426 |
* Sets the default view
|
@@ -435,17 +446,13 @@ interface Canvas
|
|
435 |
* 'FitBH' top
|
436 |
* 'FitBV' left
|
437 |
* @param array $options
|
438 |
-
*
|
439 |
-
* @return void
|
440 |
*/
|
441 |
function set_default_view($view, $options = []);
|
442 |
|
443 |
/**
|
444 |
-
* @param string $
|
445 |
-
*
|
446 |
-
* @return void
|
447 |
*/
|
448 |
-
function javascript($
|
449 |
|
450 |
/**
|
451 |
* Starts a new page
|
@@ -455,10 +462,10 @@ interface Canvas
|
|
455 |
function new_page();
|
456 |
|
457 |
/**
|
458 |
-
* Streams the PDF
|
459 |
*
|
460 |
-
* @param string $filename The filename to present to the
|
461 |
-
* @param array
|
462 |
*/
|
463 |
function stream($filename, $options = []);
|
464 |
|
@@ -466,6 +473,7 @@ interface Canvas
|
|
466 |
* Returns the PDF as a string.
|
467 |
*
|
468 |
* @param array $options Associative array: 'compress' => 1 or 0 (default 1).
|
|
|
469 |
* @return string
|
470 |
*/
|
471 |
function output($options = []);
|
24 |
*/
|
25 |
interface Canvas
|
26 |
{
|
27 |
+
/**
|
28 |
+
* @param string|float[] $paper The paper size to use as either a standard paper size (see {@link Dompdf\Adapter\CPDF::$PAPER_SIZES})
|
29 |
+
* or an array of the form `[x1, y1, x2, y2]` (typically `[0, 0, width, height]`).
|
30 |
+
* @param string $orientation The paper orientation, either `portrait` or `landscape`.
|
31 |
+
* @param Dompdf $dompdf The Dompdf instance.
|
32 |
+
*/
|
33 |
+
public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null);
|
34 |
|
35 |
/**
|
36 |
* @return Dompdf
|
45 |
function get_page_number();
|
46 |
|
47 |
/**
|
48 |
+
* Returns the total number of pages in the document
|
49 |
*
|
50 |
* @return int
|
51 |
*/
|
61 |
/**
|
62 |
* Draws a line from x1,y1 to x2,y2
|
63 |
*
|
|
|
64 |
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
65 |
+
* $style and $cap parameters (aka dash and cap).
|
66 |
*
|
67 |
+
* @param float $x1
|
68 |
+
* @param float $y1
|
69 |
+
* @param float $x2
|
70 |
+
* @param float $y2
|
71 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
72 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
73 |
+
* @param float $width
|
74 |
+
* @param array $style
|
75 |
+
* @param string $cap `butt`, `round`, or `square`
|
76 |
*/
|
77 |
+
function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt");
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Draws an arc
|
81 |
+
*
|
82 |
+
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
83 |
+
* $style and $cap parameters (aka dash and cap).
|
84 |
+
*
|
85 |
+
* @param float $x X coordinate of the arc
|
86 |
+
* @param float $y Y coordinate of the arc
|
87 |
+
* @param float $r1 Radius 1
|
88 |
+
* @param float $r2 Radius 2
|
89 |
+
* @param float $astart Start angle in degrees
|
90 |
+
* @param float $aend End angle in degrees
|
91 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
92 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
93 |
+
* @param float $width
|
94 |
+
* @param array $style
|
95 |
+
* @param string $cap `butt`, `round`, or `square`
|
96 |
+
*/
|
97 |
+
function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt");
|
98 |
|
99 |
/**
|
100 |
* Draws a rectangle at x1,y1 with width w and height h
|
101 |
*
|
102 |
+
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
103 |
+
* $style and $cap parameters (aka dash and cap).
|
|
|
104 |
*
|
105 |
+
* @param float $x1
|
106 |
+
* @param float $y1
|
107 |
+
* @param float $w
|
108 |
+
* @param float $h
|
109 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
110 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
111 |
+
* @param float $width
|
112 |
+
* @param array $style
|
113 |
+
* @param string $cap `butt`, `round`, or `square`
|
114 |
*/
|
115 |
+
function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt");
|
116 |
|
117 |
/**
|
118 |
* Draws a filled rectangle at x1,y1 with width w and height h
|
119 |
*
|
|
|
|
|
120 |
* @param float $x1
|
121 |
* @param float $y1
|
122 |
* @param float $w
|
123 |
* @param float $h
|
124 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
125 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
126 |
*/
|
127 |
function filled_rectangle($x1, $y1, $w, $h, $color);
|
128 |
|
147 |
* @param float $tr
|
148 |
* @param float $br
|
149 |
* @param float $bl
|
|
|
|
|
150 |
*/
|
151 |
function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
|
152 |
|
153 |
+
/**
|
154 |
+
* Starts a clipping polygon
|
155 |
+
*
|
156 |
+
* @param float[] $points
|
157 |
+
*/
|
158 |
+
public function clipping_polygon(array $points): void;
|
159 |
+
|
160 |
/**
|
161 |
* Ends the last clipping shape
|
162 |
*/
|
163 |
function clipping_end();
|
164 |
|
165 |
/**
|
166 |
+
* Processes a callback on every page.
|
167 |
*
|
168 |
+
* The callback function receives the four parameters `int $pageNumber`,
|
169 |
+
* `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
|
170 |
+
* that order.
|
171 |
*
|
172 |
* This function can be used to add page numbers to all pages after the
|
173 |
* first one, for example.
|
174 |
*
|
175 |
* @param callable $callback The callback function to process on every page
|
|
|
176 |
*/
|
177 |
+
public function page_script($callback): void;
|
178 |
|
179 |
/**
|
180 |
+
* Writes text at the specified x and y coordinates on every page.
|
181 |
*
|
182 |
* The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
|
183 |
* with their current values.
|
184 |
*
|
|
|
|
|
185 |
* @param float $x
|
186 |
* @param float $y
|
187 |
+
* @param string $text The text to write
|
188 |
+
* @param string $font The font file to use
|
189 |
+
* @param float $size The font size, in points
|
190 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
191 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
192 |
+
* @param float $word_space Word spacing adjustment
|
193 |
+
* @param float $char_space Char spacing adjustment
|
194 |
+
* @param float $angle Angle to write the text at, measured clockwise starting from the x-axis
|
195 |
*/
|
196 |
public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
|
197 |
|
198 |
/**
|
199 |
+
* Draws a line at the specified coordinates on every page.
|
|
|
|
|
200 |
*
|
201 |
* @param float $x1
|
202 |
* @param float $y1
|
203 |
* @param float $x2
|
204 |
* @param float $y2
|
205 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
206 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
207 |
* @param float $width
|
208 |
+
* @param array $style
|
|
|
209 |
*/
|
210 |
+
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = []);
|
211 |
|
212 |
/**
|
213 |
* Save current state
|
233 |
*
|
234 |
* @param float $angle_x
|
235 |
* @param float $angle_y
|
236 |
+
* @param float $x Origin abscissa
|
237 |
+
* @param float $y Origin ordinate
|
238 |
*/
|
239 |
function skew($angle_x, $angle_y, $x, $y);
|
240 |
|
259 |
/**
|
260 |
* Transform
|
261 |
*
|
262 |
+
* @param float $a
|
263 |
+
* @param float $b
|
264 |
+
* @param float $c
|
265 |
+
* @param float $d
|
266 |
+
* @param float $e
|
267 |
+
* @param float $f
|
|
|
268 |
*/
|
269 |
function transform($a, $b, $c, $d, $e, $f);
|
270 |
|
273 |
*
|
274 |
* The polygon is formed by joining all the points stored in the $points
|
275 |
* array. $points has the following structure:
|
276 |
+
* ```
|
277 |
* array(0 => x1,
|
278 |
* 1 => y1,
|
279 |
* 2 => x2,
|
280 |
* 3 => y2,
|
281 |
* ...
|
282 |
* );
|
283 |
+
* ```
|
284 |
*
|
285 |
+
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
286 |
+
* $style parameter (aka dash).
|
|
|
287 |
*
|
288 |
* @param array $points
|
289 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
290 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
291 |
* @param float $width
|
292 |
* @param array $style
|
293 |
+
* @param bool $fill Fills the polygon if true
|
294 |
*/
|
295 |
+
function polygon($points, $color, $width = null, $style = [], $fill = false);
|
296 |
|
297 |
/**
|
298 |
* Draws a circle at $x,$y with radius $r
|
299 |
*
|
300 |
+
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
301 |
+
* $style parameter (aka dash).
|
|
|
302 |
*
|
303 |
* @param float $x
|
304 |
* @param float $y
|
305 |
* @param float $r
|
306 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
307 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
308 |
* @param float $width
|
309 |
* @param array $style
|
310 |
+
* @param bool $fill Fills the circle if true
|
311 |
*/
|
312 |
+
function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false);
|
313 |
|
314 |
/**
|
315 |
* Add an image to the pdf.
|
317 |
* The image is placed at the specified x and y coordinates with the
|
318 |
* given width and height.
|
319 |
*
|
320 |
+
* @param string $img The path to the image
|
321 |
+
* @param float $x X position
|
322 |
+
* @param float $y Y position
|
323 |
+
* @param float $w Width
|
324 |
+
* @param float $h Height
|
325 |
* @param string $resolution The resolution of the image
|
326 |
*/
|
327 |
+
function image($img, $x, $y, $w, $h, $resolution = "normal");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
328 |
|
329 |
/**
|
330 |
* Writes text at the specified x and y coordinates
|
|
|
331 |
*
|
332 |
+
* @param float $x
|
333 |
+
* @param float $y
|
334 |
+
* @param string $text The text to write
|
335 |
+
* @param string $font The font file to use
|
336 |
+
* @param float $size The font size, in points
|
337 |
+
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
338 |
+
* where r, g, b, and alpha are float values between 0 and 1
|
339 |
+
* @param float $word_space Word spacing adjustment
|
340 |
+
* @param float $char_space Char spacing adjustment
|
341 |
+
* @param float $angle Angle to write the text at, measured clockwise starting from the x-axis
|
342 |
*/
|
343 |
function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
|
344 |
|
352 |
/**
|
353 |
* Add a link to the pdf
|
354 |
*
|
355 |
+
* @param string $url The url to link to
|
356 |
+
* @param float $x The x position of the link
|
357 |
+
* @param float $y The y position of the link
|
358 |
+
* @param float $width The width of the link
|
359 |
+
* @param float $height The height of the link
|
360 |
*/
|
361 |
function add_link($url, $x, $y, $width, $height);
|
362 |
|
363 |
/**
|
364 |
+
* Add meta information to the PDF.
|
365 |
*
|
366 |
+
* @param string $label Label of the value (Creator, Producer, etc.)
|
367 |
* @param string $value The text to set
|
368 |
*/
|
369 |
+
public function add_info(string $label, string $value): void;
|
370 |
|
371 |
/**
|
372 |
* Calculates text size, in points
|
373 |
*
|
374 |
+
* @param string $text The text to be sized
|
375 |
+
* @param string $font The font file to use
|
376 |
+
* @param float $size The font size, in points
|
377 |
+
* @param float $word_spacing Word spacing, if any
|
378 |
+
* @param float $char_spacing Char spacing, if any
|
379 |
*
|
380 |
* @return float
|
381 |
*/
|
384 |
/**
|
385 |
* Calculates font height, in points
|
386 |
*
|
387 |
+
* @param string $font The font file to use
|
388 |
+
* @param float $size The font size, in points
|
389 |
*
|
390 |
* @return float
|
391 |
*/
|
392 |
function get_font_height($font, $size);
|
393 |
|
394 |
/**
|
395 |
+
* Returns the font x-height, in points
|
396 |
*
|
397 |
+
* @param string $font The font file to use
|
398 |
+
* @param float $size The font size, in points
|
399 |
*
|
400 |
* @return float
|
401 |
*/
|
402 |
+
//function get_font_x_height($font, $size);
|
403 |
|
404 |
/**
|
405 |
+
* Calculates font baseline, in points
|
406 |
+
*
|
407 |
+
* @param string $font The font file to use
|
408 |
+
* @param float $size The font size, in points
|
409 |
*
|
410 |
* @return float
|
411 |
*/
|
412 |
+
function get_font_baseline($font, $size);
|
|
|
413 |
|
414 |
/**
|
415 |
+
* Returns the PDF's width in points
|
416 |
*
|
417 |
* @return float
|
418 |
*/
|
419 |
+
function get_width();
|
420 |
|
421 |
/**
|
422 |
+
* Returns the PDF's height in points
|
|
|
|
|
|
|
423 |
*
|
424 |
* @return float
|
425 |
*/
|
426 |
+
function get_height();
|
427 |
|
428 |
/**
|
429 |
* Sets the opacity
|
430 |
*
|
431 |
+
* @param float $opacity
|
432 |
* @param string $mode
|
433 |
*/
|
434 |
+
public function set_opacity(float $opacity, string $mode = "Normal"): void;
|
435 |
|
436 |
/**
|
437 |
* Sets the default view
|
446 |
* 'FitBH' top
|
447 |
* 'FitBV' left
|
448 |
* @param array $options
|
|
|
|
|
449 |
*/
|
450 |
function set_default_view($view, $options = []);
|
451 |
|
452 |
/**
|
453 |
+
* @param string $code
|
|
|
|
|
454 |
*/
|
455 |
+
function javascript($code);
|
456 |
|
457 |
/**
|
458 |
* Starts a new page
|
462 |
function new_page();
|
463 |
|
464 |
/**
|
465 |
+
* Streams the PDF to the client.
|
466 |
*
|
467 |
+
* @param string $filename The filename to present to the client.
|
468 |
+
* @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
|
469 |
*/
|
470 |
function stream($filename, $options = []);
|
471 |
|
473 |
* Returns the PDF as a string.
|
474 |
*
|
475 |
* @param array $options Associative array: 'compress' => 1 or 0 (default 1).
|
476 |
+
*
|
477 |
* @return string
|
478 |
*/
|
479 |
function output($options = []);
|
vendor/dompdf/dompdf/src/Cellmap.php
CHANGED
@@ -23,10 +23,8 @@ class Cellmap
|
|
23 |
{
|
24 |
/**
|
25 |
* Border style weight lookup for collapsed border resolution.
|
26 |
-
*
|
27 |
-
* @var array
|
28 |
*/
|
29 |
-
protected
|
30 |
"double" => 8,
|
31 |
"solid" => 7,
|
32 |
"dashed" => 6,
|
@@ -131,10 +129,7 @@ class Cellmap
|
|
131 |
$this->reset();
|
132 |
}
|
133 |
|
134 |
-
|
135 |
-
*
|
136 |
-
*/
|
137 |
-
public function reset()
|
138 |
{
|
139 |
$this->_num_rows = 0;
|
140 |
$this->_num_cols = 0;
|
@@ -153,10 +148,7 @@ class Cellmap
|
|
153 |
$this->__col = $this->__row = 0;
|
154 |
}
|
155 |
|
156 |
-
|
157 |
-
*
|
158 |
-
*/
|
159 |
-
public function lock_columns()
|
160 |
{
|
161 |
$this->_columns_locked = true;
|
162 |
}
|
@@ -170,9 +162,9 @@ class Cellmap
|
|
170 |
}
|
171 |
|
172 |
/**
|
173 |
-
* @param $fixed
|
174 |
*/
|
175 |
-
public function set_layout_fixed($fixed)
|
176 |
{
|
177 |
$this->_fixed_layout = $fixed;
|
178 |
}
|
@@ -456,12 +448,12 @@ class Cellmap
|
|
456 |
/**
|
457 |
* https://www.w3.org/TR/CSS21/tables.html#border-conflict-resolution
|
458 |
*
|
459 |
-
* @param int
|
460 |
-
* @param int
|
461 |
-
* @param string $h_v
|
462 |
-
* @param array
|
463 |
*/
|
464 |
-
protected function
|
465 |
{
|
466 |
if (!isset($this->_borders[$i][$j][$h_v])) {
|
467 |
$this->_borders[$i][$j][$h_v] = $border_spec;
|
@@ -483,9 +475,9 @@ class Cellmap
|
|
483 |
// width here, as its resolved width is always 0
|
484 |
if ($n_style === "hidden" || $n_width > $o_width
|
485 |
|| ($o_width == $n_width
|
486 |
-
&& isset(self
|
487 |
-
&& isset(self
|
488 |
-
&& self
|
489 |
) {
|
490 |
$this->_borders[$i][$j][$h_v] = $border_spec;
|
491 |
}
|
@@ -554,7 +546,7 @@ class Cellmap
|
|
554 |
// Recursively add the frames within the table, its row groups and rows
|
555 |
if ($frame === $this->_table
|
556 |
|| $display === "table-row"
|
557 |
-
|| in_array($display, TableFrameDecorator
|
558 |
) {
|
559 |
$start_row = $this->__row;
|
560 |
|
@@ -579,14 +571,14 @@ class Cellmap
|
|
579 |
|
580 |
// Resolve vertical borders
|
581 |
for ($i = 0; $i < $num_rows + 1; $i++) {
|
582 |
-
$this->
|
583 |
-
$this->
|
584 |
}
|
585 |
|
586 |
// Resolve horizontal borders
|
587 |
for ($j = 0; $j < $this->_num_cols; $j++) {
|
588 |
-
$this->
|
589 |
-
$this->
|
590 |
}
|
591 |
|
592 |
if ($frame === $this->_table) {
|
@@ -644,8 +636,8 @@ class Cellmap
|
|
644 |
|
645 |
if ($collapse) {
|
646 |
// Resolve vertical borders
|
647 |
-
$this->
|
648 |
-
$this->
|
649 |
}
|
650 |
}
|
651 |
|
@@ -656,8 +648,8 @@ class Cellmap
|
|
656 |
|
657 |
if ($collapse) {
|
658 |
// Resolve horizontal borders
|
659 |
-
$this->
|
660 |
-
$this->
|
661 |
}
|
662 |
}
|
663 |
|
@@ -684,11 +676,8 @@ class Cellmap
|
|
684 |
} else {
|
685 |
// The additional 1/2 width gets added to the table proper
|
686 |
[$h, $v] = $table_style->border_spacing;
|
687 |
-
|
688 |
-
$
|
689 |
-
$h = $table_style->length_in_pt($h);
|
690 |
-
$v_spacing = is_numeric($v) ? $v / 2 : $v;
|
691 |
-
$h_spacing = is_numeric($v) ? $h / 2 : $h;
|
692 |
}
|
693 |
|
694 |
foreach ($this->_frames as $frame_info) {
|
@@ -790,12 +779,19 @@ class Cellmap
|
|
790 |
}
|
791 |
}
|
792 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
793 |
}
|
794 |
|
795 |
-
|
796 |
-
*
|
797 |
-
*/
|
798 |
-
public function add_row()
|
799 |
{
|
800 |
$this->__row++;
|
801 |
$this->_num_rows++;
|
@@ -899,10 +895,7 @@ class Cellmap
|
|
899 |
$this->_frames[$g_key]["rows"] = range($first_index, $last_index);
|
900 |
}
|
901 |
|
902 |
-
|
903 |
-
*
|
904 |
-
*/
|
905 |
-
public function assign_x_positions()
|
906 |
{
|
907 |
// Pre-condition: widths must be resolved and assigned to columns and
|
908 |
// column[0]["x"] must be set.
|
@@ -918,17 +911,14 @@ class Cellmap
|
|
918 |
}
|
919 |
}
|
920 |
|
921 |
-
|
922 |
-
*
|
923 |
-
*/
|
924 |
-
public function assign_frame_heights()
|
925 |
{
|
926 |
// Pre-condition: widths and heights of each column & row must be
|
927 |
// calcluated
|
928 |
foreach ($this->_frames as $arr) {
|
929 |
$frame = $arr["frame"];
|
930 |
|
931 |
-
$h = 0;
|
932 |
foreach ($arr["rows"] as $row) {
|
933 |
if (!isset($this->_rows[$row])) {
|
934 |
// The row has been removed because of a page split, so skip it.
|
@@ -941,7 +931,7 @@ class Cellmap
|
|
941 |
if ($frame instanceof TableCellFrameDecorator) {
|
942 |
$frame->set_cell_height($h);
|
943 |
} else {
|
944 |
-
$frame->get_style()->height
|
945 |
}
|
946 |
}
|
947 |
}
|
@@ -949,13 +939,13 @@ class Cellmap
|
|
949 |
/**
|
950 |
* Re-adjust frame height if the table height is larger than its content
|
951 |
*/
|
952 |
-
public function set_frame_heights($table_height, $content_height)
|
953 |
{
|
954 |
// Distribute the increased height proportionally amongst each row
|
955 |
foreach ($this->_frames as $arr) {
|
956 |
$frame = $arr["frame"];
|
957 |
|
958 |
-
$h = 0;
|
959 |
foreach ($arr["rows"] as $row) {
|
960 |
if (!isset($this->_rows[$row])) {
|
961 |
continue;
|
@@ -967,13 +957,13 @@ class Cellmap
|
|
967 |
if ($content_height > 0) {
|
968 |
$new_height = ($h / $content_height) * $table_height;
|
969 |
} else {
|
970 |
-
$new_height = 0;
|
971 |
}
|
972 |
|
973 |
if ($frame instanceof TableCellFrameDecorator) {
|
974 |
$frame->set_cell_height($new_height);
|
975 |
} else {
|
976 |
-
$frame->get_style()->height
|
977 |
}
|
978 |
}
|
979 |
}
|
@@ -983,7 +973,7 @@ class Cellmap
|
|
983 |
*
|
984 |
* @return string
|
985 |
*/
|
986 |
-
public function __toString()
|
987 |
{
|
988 |
$str = "";
|
989 |
$str .= "Columns:<br/>";
|
23 |
{
|
24 |
/**
|
25 |
* Border style weight lookup for collapsed border resolution.
|
|
|
|
|
26 |
*/
|
27 |
+
protected const BORDER_STYLE_SCORE = [
|
28 |
"double" => 8,
|
29 |
"solid" => 7,
|
30 |
"dashed" => 6,
|
129 |
$this->reset();
|
130 |
}
|
131 |
|
132 |
+
public function reset(): void
|
|
|
|
|
|
|
133 |
{
|
134 |
$this->_num_rows = 0;
|
135 |
$this->_num_cols = 0;
|
148 |
$this->__col = $this->__row = 0;
|
149 |
}
|
150 |
|
151 |
+
public function lock_columns(): void
|
|
|
|
|
|
|
152 |
{
|
153 |
$this->_columns_locked = true;
|
154 |
}
|
162 |
}
|
163 |
|
164 |
/**
|
165 |
+
* @param bool $fixed
|
166 |
*/
|
167 |
+
public function set_layout_fixed(bool $fixed)
|
168 |
{
|
169 |
$this->_fixed_layout = $fixed;
|
170 |
}
|
448 |
/**
|
449 |
* https://www.w3.org/TR/CSS21/tables.html#border-conflict-resolution
|
450 |
*
|
451 |
+
* @param int $i
|
452 |
+
* @param int $j
|
453 |
+
* @param string $h_v `horizontal` or `vertical`
|
454 |
+
* @param array $border_spec
|
455 |
*/
|
456 |
+
protected function resolve_border(int $i, int $j, string $h_v, array $border_spec): void
|
457 |
{
|
458 |
if (!isset($this->_borders[$i][$j][$h_v])) {
|
459 |
$this->_borders[$i][$j][$h_v] = $border_spec;
|
475 |
// width here, as its resolved width is always 0
|
476 |
if ($n_style === "hidden" || $n_width > $o_width
|
477 |
|| ($o_width == $n_width
|
478 |
+
&& isset(self::BORDER_STYLE_SCORE[$n_style])
|
479 |
+
&& isset(self::BORDER_STYLE_SCORE[$o_style])
|
480 |
+
&& self::BORDER_STYLE_SCORE[$n_style] > self::BORDER_STYLE_SCORE[$o_style])
|
481 |
) {
|
482 |
$this->_borders[$i][$j][$h_v] = $border_spec;
|
483 |
}
|
546 |
// Recursively add the frames within the table, its row groups and rows
|
547 |
if ($frame === $this->_table
|
548 |
|| $display === "table-row"
|
549 |
+
|| in_array($display, TableFrameDecorator::ROW_GROUPS, true)
|
550 |
) {
|
551 |
$start_row = $this->__row;
|
552 |
|
571 |
|
572 |
// Resolve vertical borders
|
573 |
for ($i = 0; $i < $num_rows + 1; $i++) {
|
574 |
+
$this->resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
|
575 |
+
$this->resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
|
576 |
}
|
577 |
|
578 |
// Resolve horizontal borders
|
579 |
for ($j = 0; $j < $this->_num_cols; $j++) {
|
580 |
+
$this->resolve_border($start_row, $j, "horizontal", $bp["top"]);
|
581 |
+
$this->resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
|
582 |
}
|
583 |
|
584 |
if ($frame === $this->_table) {
|
636 |
|
637 |
if ($collapse) {
|
638 |
// Resolve vertical borders
|
639 |
+
$this->resolve_border($row, $this->__col, "vertical", $bp["left"]);
|
640 |
+
$this->resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]);
|
641 |
}
|
642 |
}
|
643 |
|
648 |
|
649 |
if ($collapse) {
|
650 |
// Resolve horizontal borders
|
651 |
+
$this->resolve_border($this->__row, $col, "horizontal", $bp["top"]);
|
652 |
+
$this->resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]);
|
653 |
}
|
654 |
}
|
655 |
|
676 |
} else {
|
677 |
// The additional 1/2 width gets added to the table proper
|
678 |
[$h, $v] = $table_style->border_spacing;
|
679 |
+
$v_spacing = $v / 2;
|
680 |
+
$h_spacing = $h / 2;
|
|
|
|
|
|
|
681 |
}
|
682 |
|
683 |
foreach ($this->_frames as $frame_info) {
|
779 |
}
|
780 |
}
|
781 |
}
|
782 |
+
|
783 |
+
// Adjust absolute columns so that the absolute (and max) width is the
|
784 |
+
// largest minimum width of all cells. This accounts for cells without
|
785 |
+
// absolute width within an absolute column
|
786 |
+
foreach ($this->_columns as &$col) {
|
787 |
+
if ($col["absolute"] > 0) {
|
788 |
+
$col["absolute"] = $col["min-width"];
|
789 |
+
$col["max-width"] = $col["min-width"];
|
790 |
+
}
|
791 |
+
}
|
792 |
}
|
793 |
|
794 |
+
protected function add_row(): void
|
|
|
|
|
|
|
795 |
{
|
796 |
$this->__row++;
|
797 |
$this->_num_rows++;
|
895 |
$this->_frames[$g_key]["rows"] = range($first_index, $last_index);
|
896 |
}
|
897 |
|
898 |
+
public function assign_x_positions(): void
|
|
|
|
|
|
|
899 |
{
|
900 |
// Pre-condition: widths must be resolved and assigned to columns and
|
901 |
// column[0]["x"] must be set.
|
911 |
}
|
912 |
}
|
913 |
|
914 |
+
public function assign_frame_heights(): void
|
|
|
|
|
|
|
915 |
{
|
916 |
// Pre-condition: widths and heights of each column & row must be
|
917 |
// calcluated
|
918 |
foreach ($this->_frames as $arr) {
|
919 |
$frame = $arr["frame"];
|
920 |
|
921 |
+
$h = 0.0;
|
922 |
foreach ($arr["rows"] as $row) {
|
923 |
if (!isset($this->_rows[$row])) {
|
924 |
// The row has been removed because of a page split, so skip it.
|
931 |
if ($frame instanceof TableCellFrameDecorator) {
|
932 |
$frame->set_cell_height($h);
|
933 |
} else {
|
934 |
+
$frame->get_style()->set_used("height", $h);
|
935 |
}
|
936 |
}
|
937 |
}
|
939 |
/**
|
940 |
* Re-adjust frame height if the table height is larger than its content
|
941 |
*/
|
942 |
+
public function set_frame_heights(float $table_height, float $content_height): void
|
943 |
{
|
944 |
// Distribute the increased height proportionally amongst each row
|
945 |
foreach ($this->_frames as $arr) {
|
946 |
$frame = $arr["frame"];
|
947 |
|
948 |
+
$h = 0.0;
|
949 |
foreach ($arr["rows"] as $row) {
|
950 |
if (!isset($this->_rows[$row])) {
|
951 |
continue;
|
957 |
if ($content_height > 0) {
|
958 |
$new_height = ($h / $content_height) * $table_height;
|
959 |
} else {
|
960 |
+
$new_height = 0.0;
|
961 |
}
|
962 |
|
963 |
if ($frame instanceof TableCellFrameDecorator) {
|
964 |
$frame->set_cell_height($new_height);
|
965 |
} else {
|
966 |
+
$frame->get_style()->set_used("height", $new_height);
|
967 |
}
|
968 |
}
|
969 |
}
|
973 |
*
|
974 |
* @return string
|
975 |
*/
|
976 |
+
public function __toString(): string
|
977 |
{
|
978 |
$str = "";
|
979 |
$str .= "Columns:<br/>";
|
vendor/dompdf/dompdf/src/Css/Color.php
CHANGED
@@ -165,7 +165,7 @@ class Color
|
|
165 |
];
|
166 |
|
167 |
/**
|
168 |
-
* @param $color
|
169 |
* @return array|string|null
|
170 |
*/
|
171 |
static function parse($color)
|
@@ -261,7 +261,7 @@ class Color
|
|
261 |
|
262 |
// Parse alpha value
|
263 |
if (Helpers::is_percent($alpha)) {
|
264 |
-
$alpha =
|
265 |
} else {
|
266 |
$alpha = (float) $alpha;
|
267 |
}
|
@@ -294,7 +294,7 @@ class Color
|
|
294 |
return null;
|
295 |
}
|
296 |
|
297 |
-
$values = array_map(function($c) {
|
298 |
return min(1.0, max(0.0, floatval(trim($c))));
|
299 |
}, $values);
|
300 |
|
@@ -306,7 +306,7 @@ class Color
|
|
306 |
}
|
307 |
|
308 |
/**
|
309 |
-
* @param $color
|
310 |
* @param float $alpha
|
311 |
* @return array
|
312 |
*/
|
165 |
];
|
166 |
|
167 |
/**
|
168 |
+
* @param array|string|null $color
|
169 |
* @return array|string|null
|
170 |
*/
|
171 |
static function parse($color)
|
261 |
|
262 |
// Parse alpha value
|
263 |
if (Helpers::is_percent($alpha)) {
|
264 |
+
$alpha = (float) $alpha / 100;
|
265 |
} else {
|
266 |
$alpha = (float) $alpha;
|
267 |
}
|
294 |
return null;
|
295 |
}
|
296 |
|
297 |
+
$values = array_map(function ($c) {
|
298 |
return min(1.0, max(0.0, floatval(trim($c))));
|
299 |
}, $values);
|
300 |
|
306 |
}
|
307 |
|
308 |
/**
|
309 |
+
* @param array|string $color
|
310 |
* @param float $alpha
|
311 |
* @return array
|
312 |
*/
|
vendor/dompdf/dompdf/src/Css/Style.php
CHANGED
@@ -22,36 +22,182 @@ use Dompdf\Frame;
|
|
22 |
* It includes methods to resolve colors and lengths, as well as getters &
|
23 |
* setters for many CSS properties.
|
24 |
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
* Actual CSS parsing is performed in the {@link Stylesheet} class.
|
26 |
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
* @package dompdf
|
28 |
*/
|
29 |
class Style
|
30 |
{
|
31 |
-
const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
|
32 |
-
const CSS_INTEGER = "[+-]?\d+";
|
33 |
-
const CSS_NUMBER = "[+-]?\d*\.?\d+";
|
34 |
|
35 |
/**
|
36 |
* Default font size, in points.
|
37 |
*
|
38 |
* @var float
|
39 |
*/
|
40 |
-
static $default_font_size = 12;
|
41 |
|
42 |
/**
|
43 |
* Default line height, as a fraction of the font size.
|
44 |
*
|
45 |
* @var float
|
46 |
*/
|
47 |
-
static $default_line_height = 1.2;
|
48 |
|
49 |
/**
|
50 |
* Default "absolute" font sizes relative to the default font-size
|
51 |
-
*
|
|
|
52 |
* @var array<float>
|
53 |
*/
|
54 |
-
static $font_size_keywords = [
|
55 |
"xx-small" => 0.6, // 3/5
|
56 |
"x-small" => 0.75, // 3/4
|
57 |
"small" => 0.889, // 8/9
|
@@ -62,19 +208,15 @@ class Style
|
|
62 |
];
|
63 |
|
64 |
/**
|
65 |
-
* List of valid text-align keywords.
|
66 |
-
*
|
67 |
-
* @var array
|
68 |
*/
|
69 |
-
|
70 |
|
71 |
/**
|
72 |
-
* List of valid vertical-align keywords.
|
73 |
-
*
|
74 |
-
* @var array
|
75 |
*/
|
76 |
-
|
77 |
-
"super", "text-bottom", "text-top", "top"];
|
78 |
|
79 |
/**
|
80 |
* List of all block-level (outer) display types.
|
@@ -119,25 +261,19 @@ class Style
|
|
119 |
];
|
120 |
|
121 |
/**
|
122 |
-
* List of all inline (inner) display types.
|
123 |
-
*
|
124 |
-
* @var array
|
125 |
*/
|
126 |
-
|
127 |
|
128 |
/**
|
129 |
-
* List of all block (inner) display types.
|
130 |
-
*
|
131 |
-
* @var array
|
132 |
*/
|
133 |
-
|
134 |
|
135 |
/**
|
136 |
-
* List of all table (inner) display types.
|
137 |
-
*
|
138 |
-
* @var array
|
139 |
*/
|
140 |
-
|
141 |
|
142 |
/**
|
143 |
* Lookup table for valid display types. Initially computed from the
|
@@ -148,19 +284,30 @@ class Style
|
|
148 |
protected static $valid_display_types = [];
|
149 |
|
150 |
/**
|
151 |
-
* List of all positioned types.
|
152 |
-
|
153 |
-
|
|
|
|
|
|
|
154 |
*/
|
155 |
-
|
|
|
|
|
|
|
|
|
156 |
|
157 |
/**
|
158 |
-
* List of valid
|
|
|
159 |
*
|
160 |
-
* @
|
161 |
*/
|
162 |
-
|
163 |
-
"
|
|
|
|
|
|
|
164 |
|
165 |
/**
|
166 |
* Map of CSS shorthand properties and their corresponding sub-properties.
|
@@ -181,9 +328,18 @@ class Style
|
|
181 |
"background_color"
|
182 |
],
|
183 |
"border" => [
|
184 |
-
"
|
185 |
-
"
|
186 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
],
|
188 |
"border_top" => [
|
189 |
"border_top_width",
|
@@ -238,6 +394,12 @@ class Style
|
|
238 |
"font_weight",
|
239 |
"line_height"
|
240 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
"list_style" => [
|
242 |
"list_style_image",
|
243 |
"list_style_position",
|
@@ -278,7 +440,7 @@ class Style
|
|
278 |
/**
|
279 |
* Default style values.
|
280 |
*
|
281 |
-
* @link
|
282 |
*
|
283 |
* @var array
|
284 |
*/
|
@@ -287,7 +449,7 @@ class Style
|
|
287 |
/**
|
288 |
* List of inherited properties
|
289 |
*
|
290 |
-
* @link
|
291 |
*
|
292 |
* @var array
|
293 |
*/
|
@@ -315,19 +477,20 @@ class Style
|
|
315 |
protected $_media_queries;
|
316 |
|
317 |
/**
|
318 |
-
*
|
319 |
-
* https://www.w3.org/TR/css-cascade-3/#value-stages
|
320 |
*
|
321 |
* @var array
|
322 |
*/
|
323 |
-
protected $
|
324 |
|
325 |
/**
|
326 |
-
*
|
|
|
|
|
327 |
*
|
328 |
* @var array
|
329 |
*/
|
330 |
-
protected $
|
331 |
|
332 |
/**
|
333 |
* Computed values of the CSS properties.
|
@@ -341,7 +504,15 @@ class Style
|
|
341 |
*
|
342 |
* @var array
|
343 |
*/
|
344 |
-
protected $
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
345 |
|
346 |
protected static $_dependency_map = [
|
347 |
"border_top_style" => [
|
@@ -366,6 +537,11 @@ class Style
|
|
366 |
"border_right_width",
|
367 |
"border_bottom_width",
|
368 |
"border_left_width",
|
|
|
|
|
|
|
|
|
|
|
369 |
"line_height",
|
370 |
"margin_top",
|
371 |
"margin_right",
|
@@ -376,7 +552,14 @@ class Style
|
|
376 |
"padding_top",
|
377 |
"padding_right",
|
378 |
"padding_bottom",
|
379 |
-
"padding_left"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
380 |
],
|
381 |
"float" => [
|
382 |
"display"
|
@@ -405,7 +588,7 @@ class Style
|
|
405 |
protected $parent_style;
|
406 |
|
407 |
/**
|
408 |
-
* @var Frame
|
409 |
*/
|
410 |
protected $_frame;
|
411 |
|
@@ -416,19 +599,20 @@ class Style
|
|
416 |
*/
|
417 |
protected $_origin = Stylesheet::ORIG_AUTHOR;
|
418 |
|
419 |
-
// private members
|
420 |
/**
|
421 |
* The computed bottom spacing
|
|
|
|
|
422 |
*/
|
423 |
private $_computed_bottom_spacing = null;
|
424 |
|
425 |
/**
|
426 |
-
* @var bool
|
427 |
*/
|
428 |
private $has_border_radius_cache = null;
|
429 |
|
430 |
/**
|
431 |
-
* @var array
|
432 |
*/
|
433 |
private $resolved_border_radius = null;
|
434 |
|
@@ -438,14 +622,12 @@ class Style
|
|
438 |
private $fontMetrics;
|
439 |
|
440 |
/**
|
441 |
-
*
|
442 |
-
*
|
443 |
-
* @param Stylesheet $stylesheet the stylesheet this Style is associated with.
|
444 |
-
* @param int $origin
|
445 |
*/
|
446 |
-
public function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR)
|
447 |
{
|
448 |
-
$this->
|
449 |
|
450 |
$this->_stylesheet = $stylesheet;
|
451 |
$this->_media_queries = [];
|
@@ -458,17 +640,20 @@ class Style
|
|
458 |
$d =& self::$_defaults;
|
459 |
|
460 |
// All CSS 2.1 properties, and their default values
|
|
|
|
|
|
|
461 |
$d["azimuth"] = "center";
|
462 |
$d["background_attachment"] = "scroll";
|
463 |
$d["background_color"] = "transparent";
|
464 |
$d["background_image"] = "none";
|
465 |
$d["background_image_resolution"] = "normal";
|
466 |
-
$d["background_position"] = "0% 0%";
|
467 |
$d["background_repeat"] = "repeat";
|
468 |
$d["background"] = "";
|
469 |
$d["border_collapse"] = "separate";
|
470 |
$d["border_color"] = "";
|
471 |
-
$d["border_spacing"] =
|
472 |
$d["border_style"] = "";
|
473 |
$d["border_top"] = "";
|
474 |
$d["border_right"] = "";
|
@@ -487,10 +672,10 @@ class Style
|
|
487 |
$d["border_bottom_width"] = "medium";
|
488 |
$d["border_left_width"] = "medium";
|
489 |
$d["border_width"] = "";
|
490 |
-
$d["border_bottom_left_radius"] =
|
491 |
-
$d["border_bottom_right_radius"] =
|
492 |
-
$d["border_top_left_radius"] =
|
493 |
-
$d["border_top_right_radius"] =
|
494 |
$d["border_radius"] = "";
|
495 |
$d["border"] = "";
|
496 |
$d["bottom"] = "auto";
|
@@ -518,6 +703,7 @@ class Style
|
|
518 |
$d["font"] = "";
|
519 |
$d["height"] = "auto";
|
520 |
$d["image_resolution"] = "normal";
|
|
|
521 |
$d["left"] = "auto";
|
522 |
$d["letter_spacing"] = "normal";
|
523 |
$d["line_height"] = "normal";
|
@@ -525,27 +711,27 @@ class Style
|
|
525 |
$d["list_style_position"] = "outside";
|
526 |
$d["list_style_type"] = "disc";
|
527 |
$d["list_style"] = "";
|
528 |
-
$d["margin_right"] =
|
529 |
-
$d["margin_left"] =
|
530 |
-
$d["margin_top"] =
|
531 |
-
$d["margin_bottom"] =
|
532 |
$d["margin"] = "";
|
533 |
$d["max_height"] = "none";
|
534 |
$d["max_width"] = "none";
|
535 |
$d["min_height"] = "auto";
|
536 |
$d["min_width"] = "auto";
|
537 |
-
$d["orphans"] =
|
538 |
$d["outline_color"] = "currentcolor"; // "invert" special color is not supported
|
539 |
$d["outline_style"] = "none";
|
540 |
$d["outline_width"] = "medium";
|
541 |
-
$d["outline_offset"] =
|
542 |
$d["outline"] = "";
|
543 |
$d["overflow"] = "visible";
|
544 |
$d["overflow_wrap"] = "normal";
|
545 |
-
$d["padding_top"] =
|
546 |
-
$d["padding_right"] =
|
547 |
-
$d["padding_bottom"] =
|
548 |
-
$d["padding_left"] =
|
549 |
$d["padding"] = "";
|
550 |
$d["page_break_after"] = "auto";
|
551 |
$d["page_break_before"] = "auto";
|
@@ -570,7 +756,7 @@ class Style
|
|
570 |
$d["table_layout"] = "auto";
|
571 |
$d["text_align"] = "";
|
572 |
$d["text_decoration"] = "none";
|
573 |
-
$d["text_indent"] =
|
574 |
$d["text_transform"] = "none";
|
575 |
$d["top"] = "auto";
|
576 |
$d["unicode_bidi"] = "normal";
|
@@ -579,14 +765,15 @@ class Style
|
|
579 |
$d["voice_family"] = "";
|
580 |
$d["volume"] = "medium";
|
581 |
$d["white_space"] = "normal";
|
582 |
-
$d["widows"] =
|
583 |
$d["width"] = "auto";
|
|
|
584 |
$d["word_spacing"] = "normal";
|
585 |
$d["z_index"] = "auto";
|
586 |
|
587 |
// CSS3
|
588 |
-
$d["opacity"] =
|
589 |
-
$d["background_size"] = "auto auto";
|
590 |
$d["transform"] = "none";
|
591 |
$d["transform_origin"] = "50% 50%";
|
592 |
|
@@ -642,6 +829,7 @@ class Style
|
|
642 |
"volume",
|
643 |
"white_space",
|
644 |
"widows",
|
|
|
645 |
"word_spacing",
|
646 |
];
|
647 |
|
@@ -673,24 +861,31 @@ class Style
|
|
673 |
}
|
674 |
|
675 |
/**
|
676 |
-
*
|
|
|
|
|
677 |
*/
|
678 |
-
function
|
679 |
{
|
|
|
|
|
|
|
|
|
|
|
680 |
}
|
681 |
|
682 |
/**
|
683 |
-
* @param $media_queries
|
684 |
*/
|
685 |
-
function set_media_queries($media_queries)
|
686 |
{
|
687 |
$this->_media_queries = $media_queries;
|
688 |
}
|
689 |
|
690 |
/**
|
691 |
-
* @return array
|
692 |
*/
|
693 |
-
function get_media_queries()
|
694 |
{
|
695 |
return $this->_media_queries;
|
696 |
}
|
@@ -698,23 +893,23 @@ class Style
|
|
698 |
/**
|
699 |
* @param Frame $frame
|
700 |
*/
|
701 |
-
function set_frame(Frame $frame)
|
702 |
{
|
703 |
$this->_frame = $frame;
|
704 |
}
|
705 |
|
706 |
/**
|
707 |
-
* @return Frame
|
708 |
*/
|
709 |
-
function get_frame()
|
710 |
{
|
711 |
return $this->_frame;
|
712 |
}
|
713 |
|
714 |
/**
|
715 |
-
* @param $origin
|
716 |
*/
|
717 |
-
function set_origin($origin)
|
718 |
{
|
719 |
$this->_origin = $origin;
|
720 |
}
|
@@ -722,17 +917,17 @@ class Style
|
|
722 |
/**
|
723 |
* @return int
|
724 |
*/
|
725 |
-
function get_origin()
|
726 |
{
|
727 |
return $this->_origin;
|
728 |
}
|
729 |
|
730 |
/**
|
731 |
-
*
|
732 |
*
|
733 |
* @return Stylesheet
|
734 |
*/
|
735 |
-
function get_stylesheet()
|
736 |
{
|
737 |
return $this->_stylesheet;
|
738 |
}
|
@@ -764,12 +959,12 @@ class Style
|
|
764 |
*
|
765 |
* @return float|string
|
766 |
*/
|
767 |
-
function length_in_pt($length, ?float $ref_size = null)
|
768 |
{
|
769 |
$font_size = $this->__get("font_size");
|
770 |
$ref_size = $ref_size ?? $font_size;
|
771 |
|
772 |
-
if (
|
773 |
$length = [$length];
|
774 |
}
|
775 |
|
@@ -787,13 +982,7 @@ class Style
|
|
787 |
}
|
788 |
|
789 |
$val = $this->single_length_in_pt((string) $l, $ref_size, $font_size);
|
790 |
-
|
791 |
-
// FIXME: Using the ref size as fallback here currently ensures that
|
792 |
-
// invalid widths or heights are treated as the corresponding
|
793 |
-
// containing-block dimension, which can look like the declaration
|
794 |
-
// is being ignored. Implement proper compute methods instead, and
|
795 |
-
// fall back to 0 here
|
796 |
-
$ret += $val ?? $ref_size;
|
797 |
}
|
798 |
|
799 |
return $ret;
|
@@ -816,60 +1005,78 @@ class Style
|
|
816 |
|
817 |
$key = "$l/$ref_size/$font_size";
|
818 |
|
819 |
-
if (
|
820 |
return $cache[$key];
|
821 |
}
|
822 |
|
823 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
824 |
// Legacy support for unitless values, not covered by spec. Might
|
825 |
// want to restrict this to unitless `0` in the future
|
826 |
-
$value =
|
827 |
}
|
828 |
|
829 |
-
elseif (
|
830 |
-
$value =
|
831 |
}
|
832 |
|
833 |
-
elseif (
|
834 |
$dpi = $this->_stylesheet->get_dompdf()->getOptions()->getDpi();
|
835 |
-
$value = (
|
836 |
}
|
837 |
|
838 |
-
elseif (
|
839 |
-
$value =
|
840 |
}
|
841 |
|
842 |
-
elseif (
|
843 |
-
$
|
|
|
844 |
$root_font_size = $root_style === null || $root_style === $this
|
845 |
? $font_size
|
846 |
-
: $root_style->font_size;
|
847 |
-
$value =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
848 |
}
|
849 |
|
850 |
-
elseif (
|
851 |
-
$value =
|
852 |
}
|
853 |
|
854 |
-
elseif (
|
855 |
-
$value =
|
856 |
}
|
857 |
|
858 |
-
elseif (
|
859 |
-
$value =
|
860 |
}
|
861 |
|
862 |
-
elseif (
|
863 |
// FIXME: em:ex ratio?
|
864 |
-
$value =
|
865 |
}
|
866 |
|
867 |
-
elseif (
|
868 |
-
$value =
|
869 |
}
|
870 |
|
871 |
-
elseif (
|
872 |
-
$value =
|
873 |
}
|
874 |
|
875 |
else {
|
@@ -880,7 +1087,6 @@ class Style
|
|
880 |
return $cache[$key] = $value;
|
881 |
}
|
882 |
|
883 |
-
|
884 |
/**
|
885 |
* Resolve inherited property values using the provided parent style or the
|
886 |
* default values, in case no parent style exists.
|
@@ -888,17 +1094,15 @@ class Style
|
|
888 |
* https://www.w3.org/TR/css-cascade-3/#inheriting
|
889 |
*
|
890 |
* @param Style|null $parent
|
891 |
-
*
|
892 |
-
* @return Style
|
893 |
*/
|
894 |
-
function inherit(?Style $parent = null)
|
895 |
{
|
896 |
$this->parent_style = $parent;
|
897 |
|
898 |
// Clear the computed font size, as it might depend on the parent
|
899 |
// font size
|
900 |
unset($this->_props_computed["font_size"]);
|
901 |
-
unset($this->
|
902 |
|
903 |
if ($parent) {
|
904 |
foreach (self::$_inherited as $prop) {
|
@@ -908,15 +1112,13 @@ class Style
|
|
908 |
if (isset($this->_props[$prop]) || isset(self::$_props_shorthand[$prop])) {
|
909 |
continue;
|
910 |
}
|
911 |
-
|
912 |
if (isset($parent->_props[$prop])) {
|
913 |
-
$parent_val =
|
914 |
-
|
915 |
-
: $parent->compute_prop($prop, $parent->_props[$prop]);
|
916 |
-
|
917 |
$this->_props[$prop] = $parent_val;
|
918 |
$this->_props_computed[$prop] = $parent_val;
|
919 |
-
$this->
|
920 |
}
|
921 |
}
|
922 |
}
|
@@ -924,23 +1126,19 @@ class Style
|
|
924 |
foreach ($this->_props as $prop => $val) {
|
925 |
if ($val === "inherit") {
|
926 |
if ($parent && isset($parent->_props[$prop])) {
|
927 |
-
$parent_val =
|
928 |
-
? $parent->_props_computed[$prop]
|
929 |
-
: $parent->compute_prop($prop, $parent->_props[$prop]);
|
930 |
|
931 |
$this->_props[$prop] = $parent_val;
|
932 |
$this->_props_computed[$prop] = $parent_val;
|
933 |
-
$this->
|
934 |
} else {
|
935 |
// Parent prop not set, use default
|
936 |
$this->_props[$prop] = self::$_defaults[$prop];
|
937 |
unset($this->_props_computed[$prop]);
|
938 |
-
unset($this->
|
939 |
}
|
940 |
}
|
941 |
}
|
942 |
-
|
943 |
-
return $this;
|
944 |
}
|
945 |
|
946 |
/**
|
@@ -948,7 +1146,7 @@ class Style
|
|
948 |
*
|
949 |
* @param Style $style
|
950 |
*/
|
951 |
-
function merge(Style $style)
|
952 |
{
|
953 |
foreach ($style->_props as $prop => $val) {
|
954 |
$important = isset($style->_important_props[$prop]);
|
@@ -958,89 +1156,77 @@ class Style
|
|
958 |
continue;
|
959 |
}
|
960 |
|
961 |
-
$computed = \array_key_exists($prop, $style->_props_computed)
|
962 |
-
? $style->_props_computed[$prop]
|
963 |
-
: $style->compute_prop($prop, $val);
|
964 |
-
|
965 |
-
// Skip invalid declarations. Because styles are merged into an
|
966 |
-
// initially empty style object during stylesheet loading, this
|
967 |
-
// handles all invalid declarations
|
968 |
-
if ($computed === null) {
|
969 |
-
continue;
|
970 |
-
}
|
971 |
-
|
972 |
if ($important) {
|
973 |
$this->_important_props[$prop] = true;
|
974 |
}
|
975 |
|
976 |
$this->_props[$prop] = $val;
|
977 |
|
978 |
-
//
|
979 |
-
//
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
|
|
984 |
} else {
|
985 |
-
$this->_props_computed[$prop]
|
986 |
-
$this->
|
987 |
}
|
988 |
}
|
989 |
}
|
990 |
|
991 |
/**
|
992 |
-
*
|
993 |
-
*
|
994 |
-
*
|
995 |
-
* @param string $color
|
996 |
-
* @return array|string|null
|
997 |
-
*/
|
998 |
-
function munge_color($color)
|
999 |
-
{
|
1000 |
-
return Color::parse($color);
|
1001 |
-
}
|
1002 |
-
|
1003 |
-
/**
|
1004 |
-
* @deprecated
|
1005 |
-
* @param string $prop
|
1006 |
*/
|
1007 |
-
function
|
1008 |
{
|
1009 |
-
$
|
1010 |
-
$this->_important_props[$prop] = true;
|
1011 |
}
|
1012 |
|
1013 |
/**
|
1014 |
-
*
|
1015 |
-
*
|
1016 |
-
*
|
|
|
1017 |
*/
|
1018 |
-
function
|
1019 |
{
|
1020 |
-
|
1021 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1022 |
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
|
|
|
|
1030 |
}
|
1031 |
|
1032 |
/**
|
1033 |
-
* Set
|
1034 |
*
|
1035 |
* Setting `$clear_dependencies` to `false` is useful for saving a bit of
|
1036 |
* unnecessary work while loading stylesheets.
|
1037 |
*
|
1038 |
* @param string $prop The property to set.
|
1039 |
-
* @param mixed $val The value declaration.
|
1040 |
* @param bool $important Whether the declaration is important.
|
1041 |
* @param bool $clear_dependencies Whether to clear computed values of dependent properties.
|
1042 |
*/
|
1043 |
-
function set_prop(string $prop, $val, bool $important = false, bool $clear_dependencies = true): void
|
1044 |
{
|
1045 |
$prop = str_replace("-", "_", $prop);
|
1046 |
|
@@ -1055,9 +1241,8 @@ class Style
|
|
1055 |
return;
|
1056 |
}
|
1057 |
|
1058 |
-
if ($prop !== "content" && is_string($val) && mb_strpos($val, "url") === false &&
|
1059 |
$val = mb_strtolower(trim(str_replace(["\n", "\t"], [" "], $val)));
|
1060 |
-
$val = preg_replace("/([0-9]+) (pt|px|pc|rem|em|ex|in|cm|mm|%)/S", "\\1\\2", $val);
|
1061 |
}
|
1062 |
|
1063 |
if (isset(self::$_props_shorthand[$prop])) {
|
@@ -1068,17 +1253,35 @@ class Style
|
|
1068 |
$this->set_prop($sub_prop, $val, $important, $clear_dependencies);
|
1069 |
}
|
1070 |
} else {
|
1071 |
-
$method = "
|
1072 |
-
|
1073 |
if (!isset(self::$_methods_cache[$method])) {
|
1074 |
self::$_methods_cache[$method] = method_exists($this, $method);
|
1075 |
}
|
1076 |
-
|
1077 |
if (self::$_methods_cache[$method]) {
|
1078 |
-
$this->$method($val
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1079 |
}
|
1080 |
}
|
1081 |
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1082 |
// `!important` declarations take precedence over normal ones
|
1083 |
if (!$important && isset($this->_important_props[$prop])) {
|
1084 |
return;
|
@@ -1090,7 +1293,7 @@ class Style
|
|
1090 |
|
1091 |
// https://www.w3.org/TR/css-cascade-3/#inherit-initial
|
1092 |
if ($val === "unset") {
|
1093 |
-
$val = in_array($prop, self::$_inherited, true)
|
1094 |
? "inherit"
|
1095 |
: "initial";
|
1096 |
}
|
@@ -1100,9 +1303,16 @@ class Style
|
|
1100 |
$val = self::$_defaults[$prop];
|
1101 |
}
|
1102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1103 |
$this->_props[$prop] = $val;
|
1104 |
-
|
1105 |
-
|
1106 |
|
1107 |
if ($clear_dependencies) {
|
1108 |
// Clear the computed values of any dependent properties, so
|
@@ -1110,40 +1320,24 @@ class Style
|
|
1110 |
if (isset(self::$_dependency_map[$prop])) {
|
1111 |
foreach (self::$_dependency_map[$prop] as $dependent) {
|
1112 |
unset($this->_props_computed[$dependent]);
|
1113 |
-
unset($this->
|
1114 |
}
|
1115 |
}
|
1116 |
|
1117 |
-
|
1118 |
-
// property
|
1119 |
-
if ($prop === "border_top_left_radius"
|
1120 |
-
|| $prop === "border_top_right_radius"
|
1121 |
-
|| $prop === "border_bottom_left_radius"
|
1122 |
-
|| $prop === "border_bottom_right_radius"
|
1123 |
-
) {
|
1124 |
-
$this->has_border_radius_cache = null;
|
1125 |
-
}
|
1126 |
-
}
|
1127 |
-
|
1128 |
-
// FIXME: temporary hack around lack of persistence of base href for
|
1129 |
-
// URLs. Compute value immediately, before the original base href is
|
1130 |
-
// no longer available
|
1131 |
-
if ($prop === "background_image" || $prop === "list_style_image") {
|
1132 |
-
$this->compute_prop($prop, $val);
|
1133 |
}
|
1134 |
}
|
1135 |
}
|
1136 |
|
1137 |
/**
|
1138 |
-
*
|
1139 |
-
* properties while loading stylesheets.
|
1140 |
*
|
1141 |
* @param string $prop
|
1142 |
*
|
1143 |
* @return mixed
|
1144 |
* @throws Exception
|
1145 |
*/
|
1146 |
-
function
|
1147 |
{
|
1148 |
// Legacy property aliases
|
1149 |
if (isset(self::$_props_alias[$prop])) {
|
@@ -1154,66 +1348,92 @@ class Style
|
|
1154 |
throw new Exception("'$prop' is not a recognized CSS property.");
|
1155 |
}
|
1156 |
|
1157 |
-
$
|
|
|
1158 |
|
1159 |
-
|
1160 |
-
|
1161 |
-
|
1162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1163 |
|
1164 |
-
|
|
|
1165 |
}
|
1166 |
|
1167 |
-
|
1168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1169 |
}
|
1170 |
|
1171 |
/**
|
1172 |
-
*
|
1173 |
*
|
1174 |
-
*
|
1175 |
-
* Style class to access any (CSS) property using the following syntax:
|
1176 |
-
* <code>
|
1177 |
-
* Style->margin_top = "1em";
|
1178 |
-
* echo (Style->margin_top);
|
1179 |
-
* </code>
|
1180 |
*
|
1181 |
-
*
|
1182 |
-
*
|
1183 |
-
* called directly from outside of this class.
|
1184 |
-
*
|
1185 |
-
* On each modification clear cache to return accurate setting.
|
1186 |
-
* Also affects direct settings not using __set
|
1187 |
-
* For easier finding all assignments, attempted to allowing only explicite assignment:
|
1188 |
-
* Very many uses, e.g. AbstractFrameReflower.php -> for now leave as it is
|
1189 |
-
* function __set($prop, $val) {
|
1190 |
-
* throw new Exception("Implicit replacement of assignment by __set. Not good.");
|
1191 |
-
* }
|
1192 |
-
* function props_set($prop, $val) { ... }
|
1193 |
*
|
1194 |
-
* @param string $prop
|
1195 |
-
* @param mixed
|
1196 |
*
|
|
|
1197 |
*/
|
1198 |
-
function
|
1199 |
{
|
1200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1201 |
}
|
1202 |
|
1203 |
/**
|
1204 |
-
*
|
1205 |
-
*
|
1206 |
-
* properties directly. Typically __get() is not called directly outside
|
1207 |
-
* of this class.
|
1208 |
-
* On each modification clear cache to return accurate setting.
|
1209 |
-
* Also affects direct settings not using __set
|
1210 |
*
|
1211 |
* @param string $prop
|
1212 |
*
|
1213 |
* @return mixed
|
1214 |
* @throws Exception
|
1215 |
*/
|
1216 |
-
function __get($prop)
|
1217 |
{
|
1218 |
// Legacy property aliases
|
1219 |
if (isset(self::$_props_alias[$prop])) {
|
@@ -1224,12 +1444,12 @@ class Style
|
|
1224 |
throw new Exception("'$prop' is not a recognized CSS property.");
|
1225 |
}
|
1226 |
|
1227 |
-
if (isset($this->
|
1228 |
-
return $this->
|
1229 |
}
|
1230 |
|
1231 |
-
$method = "
|
1232 |
-
|
1233 |
if (!isset(self::$_methods_cache[$method])) {
|
1234 |
self::$_methods_cache[$method] = method_exists($this, $method);
|
1235 |
}
|
@@ -1243,87 +1463,69 @@ class Style
|
|
1243 |
} else {
|
1244 |
return implode(" ", array_map(function ($sub_prop) {
|
1245 |
$val = $this->__get($sub_prop);
|
1246 |
-
return is_array($val) ? implode(" ", $val) : $val;
|
1247 |
}, self::$_props_shorthand[$prop]));
|
1248 |
}
|
1249 |
} else {
|
1250 |
-
|
1251 |
-
if (!\array_key_exists($prop, $this->_props_computed)) {
|
1252 |
-
$val = $this->_props[$prop] ?? self::$_defaults[$prop];
|
1253 |
-
$this->compute_prop($prop, $val);
|
1254 |
-
}
|
1255 |
-
|
1256 |
-
// Invalid declarations are skipped on style merge, but during
|
1257 |
-
// style parsing, styles might contain invalid declarations. Fall
|
1258 |
-
// back to the default value in that case
|
1259 |
-
$computed = $this->_props_computed[$prop]
|
1260 |
-
?? $this->compute_prop($prop, self::$_defaults[$prop]);
|
1261 |
$used = self::$_methods_cache[$method]
|
1262 |
-
? $this->$method()
|
1263 |
: $computed;
|
1264 |
|
1265 |
-
$this->
|
1266 |
return $used;
|
1267 |
}
|
1268 |
}
|
1269 |
|
1270 |
/**
|
1271 |
-
*
|
1272 |
-
*
|
1273 |
-
* If a shorthand property is specified, all of its sub-properties are set
|
1274 |
-
* to the same value.
|
1275 |
*
|
1276 |
-
* @
|
1277 |
-
* @param mixed $val
|
1278 |
*/
|
1279 |
-
function
|
1280 |
{
|
1281 |
-
//
|
1282 |
-
|
1283 |
-
|
|
|
|
|
1284 |
}
|
1285 |
|
1286 |
-
|
1287 |
-
|
|
|
1288 |
}
|
1289 |
|
1290 |
-
|
1291 |
-
|
1292 |
-
|
1293 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1294 |
} else {
|
1295 |
-
|
1296 |
}
|
1297 |
}
|
1298 |
|
1299 |
/**
|
1300 |
-
*
|
1301 |
-
*
|
|
|
1302 |
*
|
1303 |
* @return mixed The computed value.
|
1304 |
*/
|
1305 |
-
protected function
|
1306 |
{
|
1307 |
-
$this->_props_computed
|
1308 |
-
|
1309 |
-
|
1310 |
-
$method = "set_$prop";
|
1311 |
-
|
1312 |
-
if (!isset(self::$_methods_cache[$method])) {
|
1313 |
-
self::$_methods_cache[$method] = method_exists($this, $method);
|
1314 |
-
}
|
1315 |
-
|
1316 |
-
// During style merge, the parent style is not available yet, so
|
1317 |
-
// temporarily use the initial value for `inherit` properties. The
|
1318 |
-
// keyword is properly resolved during inheritance
|
1319 |
-
if ($val === "inherit") {
|
1320 |
-
$val = self::$_defaults[$prop];
|
1321 |
-
}
|
1322 |
|
1323 |
-
|
1324 |
-
$this->$method($val);
|
1325 |
-
} elseif ($val !== "") {
|
1326 |
-
$this->_props_computed[$prop] = $val;
|
1327 |
}
|
1328 |
|
1329 |
return $this->_props_computed[$prop];
|
@@ -1331,9 +1533,9 @@ class Style
|
|
1331 |
|
1332 |
/**
|
1333 |
* @param float $cbw The width of the containing block.
|
1334 |
-
* @return float|null
|
1335 |
*/
|
1336 |
-
function computed_bottom_spacing(float $cbw)
|
1337 |
{
|
1338 |
// Caching the bottom spacing independently of the given width is a bit
|
1339 |
// iffy, but should be okay, as the containing block should only
|
@@ -1352,28 +1554,43 @@ class Style
|
|
1352 |
);
|
1353 |
}
|
1354 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1355 |
/**
|
1356 |
* @return string
|
1357 |
*/
|
1358 |
-
function get_font_family_raw()
|
1359 |
{
|
1360 |
return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
|
1361 |
}
|
1362 |
|
1363 |
/**
|
1364 |
-
* Getter for the
|
|
|
1365 |
* Uses the {@link FontMetrics} class to resolve the font family into an
|
1366 |
* actual font file.
|
1367 |
*
|
1368 |
-
* @
|
|
|
1369 |
* @throws Exception
|
1370 |
*
|
1371 |
-
* @
|
1372 |
*/
|
1373 |
-
function
|
1374 |
{
|
1375 |
//TODO: we should be using the calculated prop rather than perform the entire family parsing operation again
|
1376 |
|
|
|
1377 |
$DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss();
|
1378 |
|
1379 |
// Select the appropriate font. First determine the subtype, then check
|
@@ -1391,9 +1608,9 @@ class Style
|
|
1391 |
|
1392 |
// Resolve font-style
|
1393 |
$font_style = $this->__get("font_style");
|
1394 |
-
$subtype = $
|
1395 |
|
1396 |
-
$families = preg_split("/\s*,\s*/", $
|
1397 |
|
1398 |
$font = null;
|
1399 |
foreach ($families as $family) {
|
@@ -1403,12 +1620,12 @@ class Style
|
|
1403 |
if ($DEBUGCSS) {
|
1404 |
print '(' . $family . ')';
|
1405 |
}
|
1406 |
-
$font = $
|
1407 |
|
1408 |
if ($font) {
|
1409 |
if ($DEBUGCSS) {
|
1410 |
print "<pre>[get_font_family:";
|
1411 |
-
print '(' . $
|
1412 |
print '(' . $font . ")get_font_family]\n</pre>";
|
1413 |
}
|
1414 |
return $font;
|
@@ -1419,7 +1636,7 @@ class Style
|
|
1419 |
if ($DEBUGCSS) {
|
1420 |
print '(default)';
|
1421 |
}
|
1422 |
-
$font = $
|
1423 |
|
1424 |
if ($font) {
|
1425 |
if ($DEBUGCSS) {
|
@@ -1428,224 +1645,189 @@ class Style
|
|
1428 |
return $font;
|
1429 |
}
|
1430 |
|
1431 |
-
throw new Exception("Unable to find a suitable font replacement for: '" . $
|
1432 |
}
|
1433 |
|
1434 |
/**
|
1435 |
-
* @
|
1436 |
* @return float
|
|
|
|
|
1437 |
*/
|
1438 |
-
function
|
1439 |
{
|
1440 |
-
$
|
1441 |
-
|
1442 |
-
if ($word_spacing === "normal") {
|
1443 |
-
return 0;
|
1444 |
-
}
|
1445 |
-
|
1446 |
-
if (strpos($word_spacing, "%") !== false) {
|
1447 |
-
return $word_spacing;
|
1448 |
}
|
1449 |
|
1450 |
-
|
|
|
|
|
1451 |
}
|
1452 |
|
1453 |
/**
|
1454 |
-
* @
|
1455 |
* @return float
|
|
|
|
|
1456 |
*/
|
1457 |
-
function
|
1458 |
{
|
1459 |
-
$
|
1460 |
-
|
1461 |
-
if ($letter_spacing === "normal") {
|
1462 |
-
return 0;
|
1463 |
}
|
1464 |
|
1465 |
-
|
|
|
|
|
1466 |
}
|
1467 |
|
1468 |
/**
|
1469 |
-
* @
|
1470 |
* @return float
|
|
|
|
|
1471 |
*/
|
1472 |
-
function
|
1473 |
{
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
return self::$default_line_height * $this->__get("font_size");
|
1478 |
}
|
1479 |
|
1480 |
-
|
1481 |
-
|
1482 |
-
|
|
|
1483 |
|
1484 |
-
return
|
1485 |
}
|
1486 |
|
1487 |
/**
|
1488 |
-
* @param string $
|
1489 |
-
* @param bool
|
|
|
1490 |
* @return array|string
|
1491 |
*/
|
1492 |
-
protected function
|
1493 |
{
|
1494 |
-
$
|
1495 |
-
|
1496 |
-
if ($val === "currentcolor") {
|
1497 |
// https://www.w3.org/TR/css-color-4/#resolving-other-colors
|
1498 |
if ($current_is_parent) {
|
1499 |
// Use the `color` value from the parent for the `color`
|
1500 |
// property itself
|
1501 |
return isset($this->parent_style)
|
1502 |
? $this->parent_style->__get("color")
|
1503 |
-
: $this->munge_color(self::$_defaults[
|
1504 |
}
|
1505 |
|
1506 |
return $this->__get("color");
|
1507 |
}
|
1508 |
|
1509 |
-
return $this->munge_color($
|
1510 |
}
|
1511 |
|
1512 |
/**
|
1513 |
* Returns the color as an array
|
1514 |
*
|
1515 |
* The array has the following format:
|
1516 |
-
*
|
1517 |
*
|
1518 |
-
* @
|
1519 |
-
* @return array
|
|
|
|
|
1520 |
*/
|
1521 |
-
function
|
1522 |
{
|
1523 |
-
return $this->
|
1524 |
}
|
1525 |
|
1526 |
/**
|
1527 |
* Returns the background color as an array
|
1528 |
*
|
1529 |
-
*
|
1530 |
*
|
1531 |
-
* @
|
1532 |
-
* @return array
|
|
|
|
|
1533 |
*/
|
1534 |
-
function
|
1535 |
{
|
1536 |
-
return $this->
|
1537 |
}
|
1538 |
|
1539 |
/**
|
1540 |
* Returns the background image URI, or "none"
|
1541 |
*
|
1542 |
-
* @
|
1543 |
* @return string
|
1544 |
-
*/
|
1545 |
-
function get_background_image()
|
1546 |
-
{
|
1547 |
-
return $this->_stylesheet->resolve_url($this->_props_computed["background_image"]);
|
1548 |
-
}
|
1549 |
-
|
1550 |
-
/**
|
1551 |
-
* Returns the background position as an array
|
1552 |
*
|
1553 |
-
*
|
1554 |
-
* <code>array(x,y, "x" => x, "y" => y)</code>
|
1555 |
-
*
|
1556 |
-
* @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
|
1557 |
-
* @return array
|
1558 |
*/
|
1559 |
-
function
|
1560 |
{
|
1561 |
-
|
1562 |
-
|
1563 |
-
return [
|
1564 |
-
0 => $tmp[0], "x" => $tmp[0],
|
1565 |
-
1 => $tmp[1], "y" => $tmp[1],
|
1566 |
-
];
|
1567 |
}
|
1568 |
|
1569 |
-
|
1570 |
/**
|
1571 |
-
* Returns the background size as an array
|
1572 |
-
*
|
1573 |
-
* The return value has one of the following formats:
|
1574 |
-
* <code>"cover"</code>
|
1575 |
-
* <code>"contain"</code>
|
1576 |
-
* <code>array(width,height)</code>
|
1577 |
-
*
|
1578 |
-
* @link https://www.w3.org/TR/css3-background/#background-size
|
1579 |
-
* @return string|array
|
1580 |
-
*/
|
1581 |
-
function get_background_size()
|
1582 |
-
{
|
1583 |
-
switch ($this->_props_computed["background_size"]) {
|
1584 |
-
case "cover":
|
1585 |
-
return "cover";
|
1586 |
-
case "contain":
|
1587 |
-
return "contain";
|
1588 |
-
default:
|
1589 |
-
break;
|
1590 |
-
}
|
1591 |
-
|
1592 |
-
$result = explode(" ", $this->_props_computed["background_size"]);
|
1593 |
-
return [$result[0], $result[1]];
|
1594 |
-
}
|
1595 |
-
|
1596 |
-
/**#@+
|
1597 |
* Returns the border color as an array
|
1598 |
*
|
1599 |
-
* See {@link Style::
|
1600 |
*
|
1601 |
-
* @
|
1602 |
-
* @return array
|
|
|
|
|
1603 |
*/
|
1604 |
-
function
|
1605 |
{
|
1606 |
-
return $this->
|
1607 |
}
|
1608 |
|
1609 |
/**
|
1610 |
-
* @
|
|
|
1611 |
*/
|
1612 |
-
function
|
1613 |
{
|
1614 |
-
return $this->
|
1615 |
}
|
1616 |
|
1617 |
/**
|
1618 |
-
* @
|
|
|
1619 |
*/
|
1620 |
-
function
|
1621 |
{
|
1622 |
-
return $this->
|
1623 |
}
|
1624 |
|
1625 |
/**
|
1626 |
-
* @
|
|
|
1627 |
*/
|
1628 |
-
function
|
1629 |
{
|
1630 |
-
return $this->
|
1631 |
}
|
1632 |
|
1633 |
-
/**#@-*/
|
1634 |
-
|
1635 |
/**
|
1636 |
* Return an array of all border properties.
|
1637 |
*
|
1638 |
* The returned array has the following structure:
|
1639 |
-
*
|
|
|
1640 |
* array("top" => array("width" => [border-width],
|
1641 |
* "style" => [border-style],
|
1642 |
* "color" => [border-color (array)]),
|
1643 |
* "bottom" ... )
|
1644 |
-
*
|
1645 |
*
|
1646 |
* @return array
|
1647 |
*/
|
1648 |
-
function get_border_properties()
|
1649 |
{
|
1650 |
return [
|
1651 |
"top" => [
|
@@ -1672,69 +1854,58 @@ class Style
|
|
1672 |
}
|
1673 |
|
1674 |
/**
|
1675 |
-
* Return a single border property
|
1676 |
*
|
1677 |
* @param string $side
|
1678 |
-
*
|
1679 |
-
* @return mixed
|
1680 |
*/
|
1681 |
-
protected function
|
1682 |
{
|
1683 |
-
$color = $this->__get("border_
|
1684 |
|
1685 |
-
return $this->__get("border_
|
1686 |
-
$this->__get("border_
|
1687 |
-
(is_array($color) ? $color["hex"] : $color);
|
1688 |
}
|
1689 |
|
1690 |
-
|
1691 |
* Return full border properties as a string
|
1692 |
*
|
1693 |
* Border properties are returned just as specified in CSS:
|
1694 |
-
*
|
1695 |
* e.g. "1px solid blue"
|
1696 |
*
|
1697 |
-
* @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
|
1698 |
* @return string
|
|
|
|
|
1699 |
*/
|
1700 |
-
function
|
1701 |
-
{
|
1702 |
-
return $this->_get_border("top");
|
1703 |
-
}
|
1704 |
-
|
1705 |
-
/**
|
1706 |
-
* @return mixed
|
1707 |
-
*/
|
1708 |
-
function get_border_right()
|
1709 |
{
|
1710 |
-
return $this->
|
1711 |
}
|
1712 |
|
1713 |
/**
|
1714 |
-
* @return
|
1715 |
*/
|
1716 |
-
function
|
1717 |
{
|
1718 |
-
return $this->
|
1719 |
}
|
1720 |
|
1721 |
/**
|
1722 |
-
* @return
|
1723 |
*/
|
1724 |
-
function
|
1725 |
{
|
1726 |
-
return $this->
|
1727 |
}
|
1728 |
|
1729 |
/**
|
1730 |
-
* @
|
1731 |
-
* @param float $w
|
1732 |
-
* @param float $h
|
1733 |
-
* @return float[]
|
1734 |
*/
|
1735 |
-
function
|
1736 |
{
|
1737 |
-
return $this->
|
1738 |
}
|
1739 |
|
1740 |
public function has_border_radius(): bool
|
@@ -1746,10 +1917,10 @@ class Style
|
|
1746 |
// Use a fixed ref size here. We don't know the border-box width here
|
1747 |
// and font size might be 0. Since we are only interested in whether
|
1748 |
// there is any border radius at all, this should do
|
1749 |
-
$tl = (float) $this->length_in_pt($this->border_top_left_radius,
|
1750 |
-
$tr = (float) $this->length_in_pt($this->border_top_right_radius,
|
1751 |
-
$br = (float) $this->length_in_pt($this->border_bottom_right_radius,
|
1752 |
-
$bl = (float) $this->length_in_pt($this->border_bottom_left_radius,
|
1753 |
|
1754 |
$this->has_border_radius_cache = $tl + $tr + $br + $bl > 0;
|
1755 |
return $this->has_border_radius_cache;
|
@@ -1842,61 +2013,60 @@ class Style
|
|
1842 |
/**
|
1843 |
* Returns the outline color as an array
|
1844 |
*
|
1845 |
-
* See {@link Style::
|
1846 |
*
|
1847 |
-
* @
|
1848 |
-
* @return array
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1849 |
*/
|
1850 |
-
function
|
1851 |
{
|
1852 |
-
return $
|
1853 |
}
|
1854 |
|
1855 |
/**
|
1856 |
* Return full outline properties as a string
|
1857 |
*
|
1858 |
* Outline properties are returned just as specified in CSS:
|
1859 |
-
*
|
1860 |
* e.g. "1px solid blue"
|
1861 |
*
|
1862 |
-
* @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
|
1863 |
* @return string
|
|
|
|
|
1864 |
*/
|
1865 |
-
function
|
1866 |
{
|
1867 |
$color = $this->__get("outline_color");
|
1868 |
|
1869 |
return $this->__get("outline_width") . " " .
|
1870 |
$this->__get("outline_style") . " " .
|
1871 |
-
(is_array($color) ? $color["hex"] : $color);
|
1872 |
-
}
|
1873 |
-
|
1874 |
-
/**
|
1875 |
-
* Returns border spacing as an array
|
1876 |
-
*
|
1877 |
-
* The array has the format (h_space,v_space)
|
1878 |
-
*
|
1879 |
-
* @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
|
1880 |
-
* @return array
|
1881 |
-
*/
|
1882 |
-
function get_border_spacing()
|
1883 |
-
{
|
1884 |
-
$arr = explode(" ", $this->_props_computed["border_spacing"]);
|
1885 |
-
if (count($arr) == 1) {
|
1886 |
-
$arr[1] = $arr[0];
|
1887 |
-
}
|
1888 |
-
return $arr;
|
1889 |
}
|
1890 |
|
1891 |
/**
|
1892 |
* Returns the list style image URI, or "none"
|
1893 |
*
|
1894 |
-
* @
|
1895 |
* @return string
|
|
|
|
|
1896 |
*/
|
1897 |
-
function
|
1898 |
{
|
1899 |
-
return $this->_stylesheet->resolve_url($
|
1900 |
}
|
1901 |
|
1902 |
/**
|
@@ -1927,45 +2097,48 @@ class Style
|
|
1927 |
}
|
1928 |
|
1929 |
/**
|
|
|
1930 |
* @return array|string
|
|
|
|
|
1931 |
*/
|
1932 |
-
function
|
1933 |
{
|
1934 |
-
$
|
1935 |
-
|
1936 |
-
if ($val === "none" || $val === "inherit") {
|
1937 |
-
return "none";
|
1938 |
}
|
1939 |
|
1940 |
-
return $this->parse_counter_prop($
|
1941 |
}
|
1942 |
|
1943 |
/**
|
|
|
1944 |
* @return array|string
|
|
|
|
|
1945 |
*/
|
1946 |
-
protected function
|
1947 |
{
|
1948 |
-
$
|
1949 |
-
|
1950 |
-
if ($val === "none") {
|
1951 |
-
return "none";
|
1952 |
}
|
1953 |
|
1954 |
-
return $this->parse_counter_prop($
|
1955 |
}
|
1956 |
|
1957 |
/**
|
|
|
1958 |
* @return string[]|string
|
|
|
|
|
1959 |
*/
|
1960 |
-
protected function
|
1961 |
{
|
1962 |
-
$
|
1963 |
-
|
1964 |
-
if ($val === "normal" || $val === "none") {
|
1965 |
-
return $val;
|
1966 |
}
|
1967 |
|
1968 |
-
return $this->parse_property_value($
|
1969 |
}
|
1970 |
|
1971 |
/*==============================*/
|
@@ -2007,143 +2180,175 @@ class Style
|
|
2007 |
|| preg_match("/^#|rgb\(|rgba\(|cmyk\(/", $val);
|
2008 |
}
|
2009 |
|
2010 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011 |
{
|
2012 |
-
$
|
2013 |
-
|
2014 |
-
|
2015 |
-
|
2016 |
-
if ($type !== "") {
|
2017 |
-
$prop .= "_" . $type;
|
2018 |
-
};
|
2019 |
-
return $prop;
|
2020 |
}
|
2021 |
|
2022 |
/**
|
2023 |
-
*
|
2024 |
-
*
|
2025 |
-
* Applicable for margin, border, padding, outline.
|
2026 |
-
*
|
2027 |
-
* @param string $style
|
2028 |
-
* @param string $side
|
2029 |
-
* @param string $type
|
2030 |
-
* @param mixed $val
|
2031 |
*/
|
2032 |
-
protected function
|
2033 |
{
|
2034 |
-
|
|
|
|
|
|
|
2035 |
|
2036 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2037 |
|
2038 |
-
|
2039 |
-
|
2040 |
-
|
2041 |
-
|
|
|
|
|
|
|
|
|
|
|
2042 |
|
2043 |
-
if ($
|
2044 |
-
|
2045 |
}
|
2046 |
|
2047 |
-
|
2048 |
-
|
2049 |
-
|
2050 |
-
// Border-width keywords
|
2051 |
-
if ($val === "thin") {
|
2052 |
-
$val_computed = 0.5;
|
2053 |
-
} elseif ($val === "medium") {
|
2054 |
-
$val_computed = 1.5;
|
2055 |
-
} elseif ($val === "thick") {
|
2056 |
-
$val_computed = 2.5;
|
2057 |
-
} elseif (mb_strpos($val, "%") !== false) {
|
2058 |
-
$val_computed = null;
|
2059 |
-
} else {
|
2060 |
-
$val_computed = $this->single_length_in_pt($val);
|
2061 |
-
|
2062 |
-
if ($val_computed < 0) {
|
2063 |
-
$val_computed = null;
|
2064 |
-
}
|
2065 |
-
}
|
2066 |
|
2067 |
-
|
2068 |
-
|
2069 |
-
|
2070 |
-
|
2071 |
-
|
2072 |
-
|
|
|
|
|
|
|
2073 |
|
2074 |
-
|
2075 |
-
|
2076 |
-
}
|
2077 |
-
if (in_array($val, Style::$BORDER_STYLES, true)) {
|
2078 |
-
$this->_props_computed[$prop] = $val;
|
2079 |
-
} else {
|
2080 |
-
$this->_props_computed[$prop] = null;
|
2081 |
-
}
|
2082 |
-
} elseif ($style === "margin" || $style === "padding") {
|
2083 |
-
if ($val === "none") {
|
2084 |
-
// Legacy support for `none` keyword, not covered by spec
|
2085 |
-
$val_computed = 0;
|
2086 |
-
} elseif ($style === "margin" && $val === "auto") {
|
2087 |
-
$val_computed = $val;
|
2088 |
-
} elseif (mb_strpos($val, "%") !== false) {
|
2089 |
-
$val_computed = $val;
|
2090 |
-
} else {
|
2091 |
-
$val_computed = $this->single_length_in_pt($val);
|
2092 |
|
2093 |
-
|
2094 |
-
|
2095 |
-
|
2096 |
-
}
|
2097 |
|
2098 |
-
|
2099 |
-
|
2100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2101 |
} else {
|
2102 |
-
$this->
|
|
|
|
|
|
|
|
|
2103 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2104 |
}
|
2105 |
|
2106 |
/**
|
2107 |
-
*
|
2108 |
-
*
|
2109 |
-
*
|
2110 |
-
* @param
|
|
|
|
|
|
|
2111 |
*/
|
2112 |
-
protected function
|
2113 |
{
|
2114 |
-
$v = $this->parse_property_value($
|
2115 |
|
2116 |
-
switch (count($v)) {
|
2117 |
case 1:
|
2118 |
-
|
2119 |
break;
|
2120 |
case 2:
|
2121 |
-
|
2122 |
break;
|
2123 |
case 3:
|
2124 |
-
|
2125 |
break;
|
2126 |
case 4:
|
2127 |
-
|
2128 |
break;
|
2129 |
default:
|
2130 |
-
return;
|
2131 |
}
|
2132 |
|
2133 |
-
|
2134 |
-
$this->set_prop($this->prop_name($style, "right", $type), $right, $important);
|
2135 |
-
$this->set_prop($this->prop_name($style, "bottom", $type), $bottom, $important);
|
2136 |
-
$this->set_prop($this->prop_name($style, "left", $type), $left, $important);
|
2137 |
}
|
2138 |
|
2139 |
/*======================*/
|
2140 |
|
2141 |
/**
|
2142 |
-
* https://www.w3.org/TR/CSS21/visuren.html#display-prop
|
2143 |
-
*
|
2144 |
-
* @param string $val
|
2145 |
*/
|
2146 |
-
protected function
|
2147 |
{
|
2148 |
// Make sure that common valid, but unsupported display types have an
|
2149 |
// appropriate fallback display type
|
@@ -2161,12 +2366,12 @@ class Style
|
|
2161 |
}
|
2162 |
|
2163 |
if (!isset(self::$valid_display_types[$val])) {
|
2164 |
-
return;
|
2165 |
}
|
2166 |
|
2167 |
// https://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
|
2168 |
if ($this->is_in_flow()) {
|
2169 |
-
|
2170 |
} else {
|
2171 |
switch ($val) {
|
2172 |
case "inline":
|
@@ -2179,129 +2384,75 @@ class Style
|
|
2179 |
// case "table-column-group":
|
2180 |
// case "table-column":
|
2181 |
// case "table-caption":
|
2182 |
-
|
2183 |
-
break;
|
2184 |
case "inline-table":
|
2185 |
-
|
2186 |
-
break;
|
2187 |
default:
|
2188 |
-
|
2189 |
-
break;
|
2190 |
}
|
2191 |
}
|
2192 |
-
|
2193 |
-
$this->_props_computed["display"] = $computed;
|
2194 |
-
}
|
2195 |
-
|
2196 |
-
protected function set_prop_color($prop, $val)
|
2197 |
-
{
|
2198 |
-
$this->_prop_cache[$prop] = null;
|
2199 |
-
|
2200 |
-
// https://www.w3.org/TR/css-color-4/#resolving-other-colors
|
2201 |
-
$munged_color = $val !== "currentcolor"
|
2202 |
-
? $this->munge_color($val)
|
2203 |
-
: $val;
|
2204 |
-
|
2205 |
-
if (is_null($munged_color)) {
|
2206 |
-
$this->_props_computed[$prop] = null;
|
2207 |
-
return;
|
2208 |
-
}
|
2209 |
-
|
2210 |
-
$this->_props_computed[$prop] = is_array($munged_color) ? $munged_color["hex"] : $munged_color;
|
2211 |
}
|
2212 |
|
2213 |
/**
|
2214 |
-
*
|
2215 |
-
*
|
2216 |
-
* The color parameter can be any valid CSS color value
|
2217 |
-
*
|
2218 |
-
* @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
|
2219 |
-
* @param string $color
|
2220 |
*/
|
2221 |
-
function
|
2222 |
{
|
2223 |
-
$this->
|
2224 |
}
|
2225 |
|
2226 |
/**
|
2227 |
-
*
|
2228 |
-
*
|
2229 |
-
* @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
|
2230 |
-
* @param string $color
|
2231 |
*/
|
2232 |
-
function
|
2233 |
{
|
2234 |
-
$this->
|
2235 |
}
|
2236 |
|
2237 |
/**
|
2238 |
-
* Set the background image url
|
2239 |
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
|
2240 |
-
*
|
2241 |
-
* @param string $val
|
2242 |
*/
|
2243 |
-
function
|
2244 |
{
|
2245 |
-
$this->_prop_cache["background_image"] = null;
|
2246 |
-
|
2247 |
$parsed_val = $this->_stylesheet->resolve_url($val);
|
2248 |
|
2249 |
if ($parsed_val === "none") {
|
2250 |
-
|
2251 |
} else {
|
2252 |
-
|
2253 |
}
|
2254 |
}
|
2255 |
|
2256 |
/**
|
2257 |
-
*
|
2258 |
-
*
|
2259 |
-
* @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
|
2260 |
-
* @param string $val
|
2261 |
*/
|
2262 |
-
function
|
2263 |
{
|
2264 |
-
$
|
2265 |
-
|
2266 |
-
if ($val === "inherit") {
|
2267 |
-
$this->_props_computed["background_repeat"] = null;
|
2268 |
-
return;
|
2269 |
-
}
|
2270 |
-
|
2271 |
-
$this->_props_computed["background_repeat"] = $val;
|
2272 |
}
|
2273 |
|
2274 |
/**
|
2275 |
-
*
|
2276 |
-
*
|
2277 |
-
* @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
|
2278 |
-
* @param string $val
|
2279 |
*/
|
2280 |
-
function
|
2281 |
{
|
2282 |
-
$
|
2283 |
-
|
2284 |
-
if ($val === "inherit") {
|
2285 |
-
$this->_props_computed["background_attachment"] = null;
|
2286 |
-
return;
|
2287 |
-
}
|
2288 |
-
|
2289 |
-
$this->_props_computed["background_attachment"] = $val;
|
2290 |
}
|
2291 |
|
2292 |
/**
|
2293 |
-
*
|
2294 |
-
*
|
2295 |
-
* @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
|
2296 |
-
* @param string $val
|
2297 |
*/
|
2298 |
-
function
|
2299 |
{
|
2300 |
-
$
|
2301 |
|
2302 |
-
|
|
|
|
|
2303 |
|
2304 |
-
switch ($
|
2305 |
case "left":
|
2306 |
$x = "0%";
|
2307 |
break;
|
@@ -2324,12 +2475,12 @@ class Style
|
|
2324 |
break;
|
2325 |
|
2326 |
default:
|
2327 |
-
$x = $
|
2328 |
break;
|
2329 |
}
|
2330 |
|
2331 |
-
if (isset($
|
2332 |
-
switch ($
|
2333 |
case "left":
|
2334 |
$x = "0%";
|
2335 |
break;
|
@@ -2347,7 +2498,7 @@ class Style
|
|
2347 |
break;
|
2348 |
|
2349 |
case "center":
|
2350 |
-
if ($
|
2351 |
$y = "50%";
|
2352 |
} else {
|
2353 |
$x = "50%";
|
@@ -2355,7 +2506,7 @@ class Style
|
|
2355 |
break;
|
2356 |
|
2357 |
default:
|
2358 |
-
$y = $
|
2359 |
break;
|
2360 |
}
|
2361 |
} else {
|
@@ -2369,118 +2520,104 @@ class Style
|
|
2369 |
if (!isset($y)) {
|
2370 |
$y = "0%";
|
2371 |
}
|
2372 |
-
|
2373 |
-
|
2374 |
}
|
2375 |
|
2376 |
/**
|
2377 |
-
*
|
2378 |
*
|
2379 |
-
*
|
2380 |
-
*
|
|
|
|
|
|
|
|
|
2381 |
*/
|
2382 |
-
function
|
2383 |
{
|
2384 |
-
$
|
|
|
|
|
2385 |
|
2386 |
-
$
|
2387 |
-
$width = $result[0];
|
2388 |
|
2389 |
-
|
2390 |
-
|
2391 |
-
|
2392 |
-
|
2393 |
-
|
2394 |
-
|
2395 |
-
|
2396 |
-
return;
|
2397 |
}
|
2398 |
|
2399 |
-
|
2400 |
-
|
|
|
2401 |
}
|
2402 |
|
2403 |
-
$
|
2404 |
-
|
2405 |
-
$height = (float)$this->length_in_pt($height);
|
2406 |
}
|
2407 |
|
2408 |
-
|
2409 |
}
|
2410 |
|
2411 |
/**
|
2412 |
-
*
|
2413 |
-
*
|
2414 |
-
* @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
|
2415 |
-
* @param string $value
|
2416 |
-
* @param bool $important
|
2417 |
*/
|
2418 |
-
function
|
2419 |
{
|
2420 |
-
|
2421 |
-
|
2422 |
-
|
2423 |
-
|
2424 |
-
|
2425 |
-
$
|
2426 |
-
|
2427 |
-
|
2428 |
-
|
2429 |
-
|
2430 |
-
|
2431 |
-
|
2432 |
-
|
2433 |
-
|
2434 |
-
|
2435 |
-
$this->set_prop("background_color", $val, $important);
|
2436 |
-
} else {
|
2437 |
-
$pos_size[] = $val;
|
2438 |
-
}
|
2439 |
}
|
|
|
2440 |
|
2441 |
-
|
2442 |
-
|
2443 |
-
|
2444 |
|
2445 |
-
|
2446 |
-
|
2447 |
-
|
2448 |
-
|
2449 |
-
|
2450 |
-
|
2451 |
-
|
2452 |
|
2453 |
-
|
2454 |
|
2455 |
-
|
2456 |
-
|
2457 |
-
}
|
2458 |
}
|
2459 |
}
|
|
|
|
|
2460 |
}
|
2461 |
|
2462 |
/**
|
2463 |
-
*
|
2464 |
-
*
|
2465 |
-
* $size can be any acceptable CSS size
|
2466 |
-
*
|
2467 |
-
* @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
|
2468 |
-
* @param string|float $size
|
2469 |
*/
|
2470 |
-
function
|
2471 |
{
|
2472 |
-
$this->_prop_cache["font_size"] = null;
|
2473 |
-
|
2474 |
-
if ($size === "inherit") {
|
2475 |
-
$this->_props_computed["font_size"] = null;
|
2476 |
-
return;
|
2477 |
-
}
|
2478 |
-
|
2479 |
$parent_font_size = isset($this->parent_style)
|
2480 |
? $this->parent_style->__get("font_size")
|
2481 |
: self::$default_font_size;
|
2482 |
|
2483 |
-
switch (
|
2484 |
case "xx-small":
|
2485 |
case "x-small":
|
2486 |
case "small":
|
@@ -2504,23 +2641,14 @@ class Style
|
|
2504 |
break;
|
2505 |
}
|
2506 |
|
2507 |
-
|
2508 |
}
|
2509 |
|
2510 |
/**
|
2511 |
-
*
|
2512 |
-
*
|
2513 |
-
* @param string|int $weight
|
2514 |
*/
|
2515 |
-
function
|
2516 |
{
|
2517 |
-
$this->_prop_cache["font_weight"] = null;
|
2518 |
-
|
2519 |
-
if ($weight === "inherit") {
|
2520 |
-
$this->_props_computed["font_weight"] = null;
|
2521 |
-
return;
|
2522 |
-
}
|
2523 |
-
|
2524 |
$computed_weight = $weight;
|
2525 |
|
2526 |
if ($weight === "bolder") {
|
@@ -2531,80 +2659,99 @@ class Style
|
|
2531 |
$computed_weight = "normal";
|
2532 |
}
|
2533 |
|
2534 |
-
|
2535 |
}
|
2536 |
|
2537 |
/**
|
2538 |
-
*
|
2539 |
*
|
2540 |
-
*
|
2541 |
-
* set individual attributes also, respecting !important mark
|
2542 |
-
* exactly this order, separate by space. Multiple fonts separated by comma:
|
2543 |
-
* font-style, font-variant, font-weight, font-size, line-height, font-family
|
2544 |
-
*
|
2545 |
-
* Other than with border and list, existing partial attributes should
|
2546 |
-
* reset when starting here, even when not mentioned.
|
2547 |
-
* If individual attribute is !important and explicit or implicit replacement is not,
|
2548 |
-
* keep individual attribute
|
2549 |
-
*
|
2550 |
-
* require whitespace as delimiters for single value attributes
|
2551 |
-
* On delimiter "/" treat first as font height, second as line height
|
2552 |
-
* treat all remaining at the end of line as font
|
2553 |
-
* font-style, font-variant, font-weight, font-size, line-height, font-family
|
2554 |
-
*
|
2555 |
-
* missing font-size and font-family might be not allowed, but accept it here and
|
2556 |
-
* use default (medium size, empty font name)
|
2557 |
*
|
2558 |
* @link https://www.w3.org/TR/CSS21/fonts.html#font-shorthand
|
2559 |
-
* @param string $val
|
2560 |
-
* @param bool $important
|
2561 |
*/
|
2562 |
-
function
|
2563 |
{
|
2564 |
-
|
2565 |
-
|
2566 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2567 |
}
|
2568 |
|
2569 |
-
|
2570 |
-
|
2571 |
-
|
2572 |
}
|
2573 |
|
2574 |
-
//
|
2575 |
-
|
2576 |
-
|
2577 |
-
)
|
2578 |
-
|
2579 |
-
|
|
|
|
|
2580 |
}
|
2581 |
|
2582 |
-
|
2583 |
-
$
|
2584 |
-
|
2585 |
-
|
2586 |
-
|
2587 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2588 |
}
|
2589 |
}
|
2590 |
|
2591 |
-
|
2592 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2593 |
}
|
|
|
|
|
|
|
|
|
2594 |
}
|
2595 |
|
2596 |
/**
|
2597 |
-
*
|
2598 |
*
|
2599 |
* If no alignment is set on the element and the direction is rtl then
|
2600 |
* the property is set to "right", otherwise it is set to "left".
|
2601 |
*
|
2602 |
* @link https://www.w3.org/TR/CSS21/text.html#propdef-text-align
|
2603 |
*/
|
2604 |
-
|
2605 |
{
|
2606 |
-
$this->_prop_cache["text_align"] = null;
|
2607 |
-
|
2608 |
$alignment = $val;
|
2609 |
if ($alignment === "") {
|
2610 |
$alignment = "left";
|
@@ -2613,607 +2760,577 @@ class Style
|
|
2613 |
}
|
2614 |
}
|
2615 |
|
2616 |
-
if (
|
2617 |
-
|
2618 |
-
return;
|
2619 |
}
|
2620 |
|
2621 |
-
|
2622 |
}
|
2623 |
|
2624 |
/**
|
2625 |
-
*
|
2626 |
-
*
|
2627 |
-
* @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
|
2628 |
-
* @param $val
|
2629 |
*/
|
2630 |
-
function
|
2631 |
{
|
2632 |
-
$
|
2633 |
-
|
2634 |
-
if ($val === "inherit") {
|
2635 |
-
$this->_props_computed["word_spacing"] = null;
|
2636 |
-
return;
|
2637 |
}
|
2638 |
|
2639 |
-
|
2640 |
-
$this->_props_computed["word_spacing"] = $val;
|
2641 |
-
} else {
|
2642 |
-
$this->_props_computed["word_spacing"] = ((float)$this->length_in_pt($val, $this->__get("font_size"))) . "pt";
|
2643 |
-
}
|
2644 |
}
|
2645 |
|
2646 |
/**
|
2647 |
-
*
|
2648 |
-
*
|
2649 |
-
* @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
|
2650 |
-
* @param $val
|
2651 |
*/
|
2652 |
-
function
|
2653 |
{
|
2654 |
-
$this->_prop_cache["letter_spacing"] = null;
|
2655 |
-
|
2656 |
-
if ($val === "inherit") {
|
2657 |
-
$this->_props_computed["letter_spacing"] = null;
|
2658 |
-
return;
|
2659 |
-
}
|
2660 |
-
|
2661 |
if ($val === "normal") {
|
2662 |
-
|
2663 |
-
} else {
|
2664 |
-
$this->_props_computed["letter_spacing"] = ((float)$this->length_in_pt($val, $this->__get("font_size"))) . "pt";
|
2665 |
}
|
|
|
|
|
2666 |
}
|
2667 |
|
2668 |
/**
|
2669 |
-
*
|
2670 |
-
*
|
2671 |
-
* @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
|
2672 |
-
* @param $val
|
2673 |
*/
|
2674 |
-
function
|
2675 |
{
|
2676 |
-
$
|
2677 |
-
|
2678 |
-
if ($val === "inherit") {
|
2679 |
-
$this->_props_computed["line_height"] = null;
|
2680 |
-
return;
|
2681 |
}
|
2682 |
|
2683 |
-
|
2684 |
-
|
2685 |
-
|
2686 |
-
$this->_props_computed["line_height"] = ((float)$this->length_in_pt($val, $this->__get("font_size"))) . "pt";
|
2687 |
}
|
|
|
|
|
|
|
|
|
2688 |
}
|
2689 |
|
2690 |
/**
|
2691 |
-
*
|
2692 |
-
*
|
2693 |
-
* @link http://www.w3.org/TR/CSS21/page.html#page-breaks
|
2694 |
-
* @param string $break
|
2695 |
*/
|
2696 |
-
function
|
2697 |
{
|
2698 |
-
$this->
|
2699 |
-
|
2700 |
-
if ($break === "inherit") {
|
2701 |
-
$this->_props_computed["page_break_before"] = null;
|
2702 |
-
return;
|
2703 |
-
}
|
2704 |
|
|
|
|
|
|
|
|
|
|
|
2705 |
if ($break === "left" || $break === "right") {
|
2706 |
$break = "always";
|
2707 |
}
|
2708 |
|
2709 |
-
|
2710 |
}
|
2711 |
|
2712 |
/**
|
2713 |
-
* @
|
2714 |
*/
|
2715 |
-
function
|
2716 |
{
|
2717 |
-
$this->_prop_cache["page_break_after"] = null;
|
2718 |
-
|
2719 |
-
if ($break === "inherit") {
|
2720 |
-
$this->_props_computed["page_break_after"] = null;
|
2721 |
-
return;
|
2722 |
-
}
|
2723 |
-
|
2724 |
if ($break === "left" || $break === "right") {
|
2725 |
$break = "always";
|
2726 |
}
|
2727 |
|
2728 |
-
|
2729 |
}
|
2730 |
|
2731 |
/**
|
2732 |
-
*
|
2733 |
-
*
|
2734 |
-
* @link http://www.w3.org/TR/CSS21/box.html#margin-properties
|
2735 |
-
* @param $val
|
2736 |
*/
|
2737 |
-
function
|
2738 |
{
|
2739 |
-
$
|
|
|
|
|
|
|
|
|
2740 |
}
|
2741 |
|
2742 |
/**
|
2743 |
-
* @
|
2744 |
*/
|
2745 |
-
function
|
2746 |
{
|
2747 |
-
$
|
|
|
|
|
|
|
|
|
2748 |
}
|
2749 |
|
2750 |
/**
|
2751 |
-
* @
|
2752 |
*/
|
2753 |
-
function
|
2754 |
{
|
2755 |
-
|
|
|
|
|
|
|
|
|
|
|
2756 |
}
|
2757 |
|
2758 |
/**
|
2759 |
-
* @
|
2760 |
*/
|
2761 |
-
function
|
2762 |
{
|
2763 |
-
|
|
|
|
|
|
|
|
|
|
|
2764 |
}
|
2765 |
|
2766 |
/**
|
2767 |
-
* @
|
2768 |
-
* @param bool $important
|
2769 |
*/
|
2770 |
-
function
|
2771 |
{
|
2772 |
-
|
|
|
|
|
|
|
|
|
|
|
2773 |
}
|
2774 |
|
2775 |
/**
|
2776 |
-
*
|
2777 |
-
*
|
2778 |
-
* @link http://www.w3.org/TR/CSS21/box.html#padding-properties
|
2779 |
-
* @param $val
|
2780 |
*/
|
2781 |
-
function
|
2782 |
{
|
2783 |
-
|
|
|
|
|
|
|
|
|
|
|
2784 |
}
|
2785 |
|
2786 |
/**
|
2787 |
-
* @
|
|
|
2788 |
*/
|
2789 |
-
function
|
2790 |
{
|
2791 |
-
$this->
|
2792 |
}
|
2793 |
|
2794 |
/**
|
2795 |
-
* @param $val
|
|
|
2796 |
*/
|
2797 |
-
function
|
2798 |
{
|
2799 |
-
$
|
|
|
|
|
|
|
|
|
2800 |
}
|
2801 |
|
2802 |
-
|
2803 |
-
* @param $val
|
2804 |
-
*/
|
2805 |
-
function set_padding_left($val)
|
2806 |
{
|
2807 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2808 |
}
|
2809 |
|
2810 |
/**
|
2811 |
-
* @
|
2812 |
-
* @
|
2813 |
*/
|
2814 |
-
function
|
2815 |
{
|
2816 |
-
$this->
|
2817 |
}
|
2818 |
|
2819 |
/**
|
2820 |
-
*
|
2821 |
-
*
|
2822 |
-
* @param string $side
|
2823 |
-
* @param string $border_spec ([width] [style] [color])
|
2824 |
-
* @param bool $important
|
2825 |
*/
|
2826 |
-
protected function
|
2827 |
{
|
2828 |
-
|
|
|
|
|
|
|
2829 |
|
2830 |
-
|
2831 |
-
|
2832 |
-
$this->set_prop("border_${side}_style", $val, $important);
|
2833 |
-
} elseif ($this->is_color_value($val)) {
|
2834 |
-
$this->set_prop("border_${side}_color", $val, $important);
|
2835 |
-
} else {
|
2836 |
-
// Assume width
|
2837 |
-
$this->set_prop("border_${side}_width", $val, $important);
|
2838 |
-
}
|
2839 |
}
|
|
|
|
|
2840 |
}
|
2841 |
|
2842 |
-
|
2843 |
-
* @link http://www.w3.org/TR/CSS21/box.html#border-properties
|
2844 |
-
* @param string $val
|
2845 |
-
* @param bool $important
|
2846 |
-
*/
|
2847 |
-
function set_border_top($val, bool $important = false)
|
2848 |
{
|
2849 |
-
$this->
|
2850 |
}
|
2851 |
|
2852 |
-
function
|
2853 |
{
|
2854 |
-
$this->
|
2855 |
}
|
2856 |
|
2857 |
-
function
|
2858 |
{
|
2859 |
-
$this->
|
2860 |
}
|
2861 |
|
2862 |
-
function
|
2863 |
{
|
2864 |
-
$this->
|
2865 |
}
|
2866 |
|
2867 |
/**
|
2868 |
-
* @
|
2869 |
-
* @
|
2870 |
*/
|
2871 |
-
function
|
2872 |
{
|
2873 |
-
$this->
|
2874 |
}
|
2875 |
|
2876 |
-
|
|
|
|
|
|
|
|
|
2877 |
{
|
2878 |
-
|
|
|
|
|
|
|
|
|
|
|
2879 |
}
|
2880 |
|
2881 |
-
function
|
2882 |
{
|
2883 |
-
$this->
|
2884 |
}
|
2885 |
|
2886 |
-
function
|
2887 |
{
|
2888 |
-
$this->
|
2889 |
}
|
2890 |
|
2891 |
-
|
2892 |
-
* @param string $val
|
2893 |
-
* @param bool $important
|
2894 |
-
*/
|
2895 |
-
function set_border_bottom($val, bool $important = false)
|
2896 |
{
|
2897 |
-
$this->
|
2898 |
}
|
2899 |
|
2900 |
-
function
|
2901 |
{
|
2902 |
-
$this->
|
2903 |
}
|
2904 |
|
2905 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
2906 |
{
|
2907 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2908 |
}
|
2909 |
|
2910 |
-
|
|
|
|
|
|
|
|
|
2911 |
{
|
2912 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2913 |
}
|
2914 |
|
2915 |
/**
|
2916 |
-
* @param string $
|
2917 |
-
* @param
|
|
|
2918 |
*/
|
2919 |
-
function
|
2920 |
{
|
2921 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
2922 |
}
|
2923 |
|
2924 |
-
function
|
2925 |
{
|
2926 |
-
$this->
|
2927 |
}
|
2928 |
|
2929 |
-
function
|
2930 |
{
|
2931 |
-
$this->
|
2932 |
}
|
2933 |
|
2934 |
-
function
|
2935 |
{
|
2936 |
-
$this->
|
2937 |
}
|
2938 |
|
2939 |
-
|
2940 |
-
* @param string $val
|
2941 |
-
* @param bool $important
|
2942 |
-
*/
|
2943 |
-
function set_border($val, bool $important = false)
|
2944 |
{
|
2945 |
-
$this->
|
2946 |
-
$this->_set_border("right", $val, $important);
|
2947 |
-
$this->_set_border("bottom", $val, $important);
|
2948 |
-
$this->_set_border("left", $val, $important);
|
2949 |
}
|
2950 |
|
2951 |
/**
|
2952 |
-
* @
|
2953 |
-
* @param bool $important
|
2954 |
*/
|
2955 |
-
function
|
2956 |
{
|
2957 |
-
$this->
|
2958 |
}
|
2959 |
|
2960 |
-
|
2961 |
-
* @param string $val
|
2962 |
-
* @param bool $important
|
2963 |
-
*/
|
2964 |
-
function set_border_color($val, bool $important = false)
|
2965 |
{
|
2966 |
-
$this->
|
2967 |
}
|
2968 |
|
2969 |
-
|
2970 |
-
* @param string $val
|
2971 |
-
* @param bool $important
|
2972 |
-
*/
|
2973 |
-
function set_border_style($val, bool $important = false)
|
2974 |
{
|
2975 |
-
$this->
|
2976 |
}
|
2977 |
|
2978 |
-
|
2979 |
-
* Sets the border radius size
|
2980 |
-
*
|
2981 |
-
* http://www.w3.org/TR/css3-background/#corners
|
2982 |
-
*
|
2983 |
-
* @param string $val
|
2984 |
-
*/
|
2985 |
-
function set_border_top_left_radius($val)
|
2986 |
{
|
2987 |
-
$this->
|
2988 |
}
|
2989 |
|
2990 |
-
|
2991 |
-
* @param string $val
|
2992 |
-
*/
|
2993 |
-
function set_border_top_right_radius($val)
|
2994 |
{
|
2995 |
-
$this->
|
2996 |
}
|
2997 |
|
2998 |
/**
|
2999 |
-
* @
|
3000 |
*/
|
3001 |
-
function
|
3002 |
{
|
3003 |
-
$this->
|
3004 |
}
|
3005 |
|
3006 |
-
|
3007 |
-
* @param string $val
|
3008 |
-
*/
|
3009 |
-
function set_border_bottom_right_radius($val)
|
3010 |
{
|
3011 |
-
$this->
|
3012 |
}
|
3013 |
|
3014 |
-
|
3015 |
-
* @param string $val
|
3016 |
-
* @param bool $important
|
3017 |
-
*/
|
3018 |
-
function set_border_radius($val, bool $important = false)
|
3019 |
{
|
3020 |
-
|
|
|
3021 |
|
3022 |
-
|
3023 |
-
|
3024 |
-
|
3025 |
-
|
3026 |
-
case 2:
|
3027 |
-
[$tl, $tr, $br, $bl] = [$r[0], $r[1], $r[0], $r[1]];
|
3028 |
-
break;
|
3029 |
-
case 3:
|
3030 |
-
[$tl, $tr, $br, $bl] = [$r[0], $r[1], $r[2], $r[1]];
|
3031 |
-
break;
|
3032 |
-
case 4:
|
3033 |
-
[$tl, $tr, $br, $bl] = [$r[0], $r[1], $r[2], $r[3]];
|
3034 |
-
break;
|
3035 |
-
default:
|
3036 |
-
return;
|
3037 |
-
}
|
3038 |
|
3039 |
-
|
3040 |
-
|
3041 |
-
$this->
|
3042 |
-
$this->set_prop("border_bottom_left_radius", $bl, $important);
|
3043 |
}
|
3044 |
|
3045 |
/**
|
3046 |
-
* @
|
3047 |
-
* @param string $corner
|
3048 |
*/
|
3049 |
-
protected function
|
3050 |
{
|
3051 |
-
$
|
|
|
3052 |
|
3053 |
-
|
|
|
|
|
|
|
3054 |
|
3055 |
-
|
3056 |
-
|
3057 |
-
|
3058 |
-
|
3059 |
|
3060 |
-
|
3061 |
-
|
3062 |
-
|
|
|
3063 |
|
3064 |
-
|
|
|
|
|
3065 |
}
|
3066 |
|
3067 |
/**
|
3068 |
-
* @
|
|
|
3069 |
*/
|
3070 |
-
function
|
3071 |
{
|
3072 |
-
return $this->
|
3073 |
}
|
3074 |
|
3075 |
-
|
3076 |
-
* @return float|int|string
|
3077 |
-
*/
|
3078 |
-
function get_border_top_right_radius()
|
3079 |
{
|
3080 |
-
return $this->
|
3081 |
}
|
3082 |
|
3083 |
-
|
3084 |
-
* @return float|int|string
|
3085 |
-
*/
|
3086 |
-
function get_border_bottom_left_radius()
|
3087 |
{
|
3088 |
-
return $this->
|
3089 |
}
|
3090 |
|
3091 |
-
|
3092 |
-
* @return float|int|string
|
3093 |
-
*/
|
3094 |
-
function get_border_bottom_right_radius()
|
3095 |
{
|
3096 |
-
return $this->
|
|
|
|
|
|
|
|
|
|
|
3097 |
}
|
3098 |
|
3099 |
/**
|
3100 |
-
* @
|
3101 |
-
* @
|
3102 |
*/
|
3103 |
-
protected function
|
3104 |
{
|
3105 |
-
$
|
3106 |
|
3107 |
-
if (
|
3108 |
-
return
|
3109 |
}
|
3110 |
|
3111 |
-
return
|
3112 |
}
|
3113 |
|
3114 |
-
|
3115 |
-
* Sets the outline styles
|
3116 |
-
*
|
3117 |
-
* @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines
|
3118 |
-
* @param string $value
|
3119 |
-
* @param bool $important
|
3120 |
-
*/
|
3121 |
-
function set_outline($value, bool $important = false)
|
3122 |
{
|
3123 |
-
|
3124 |
-
|
3125 |
-
foreach ($components as $val) {
|
3126 |
-
if (in_array($val, self::$BORDER_STYLES, true)) {
|
3127 |
-
$this->set_prop("outline_style", $val, $important);
|
3128 |
-
} elseif ($this->is_color_value($val)) {
|
3129 |
-
$this->set_prop("outline_color", $val, $important);
|
3130 |
-
} else {
|
3131 |
-
// Assume width
|
3132 |
-
$this->set_prop("outline_width", $val, $important);
|
3133 |
-
}
|
3134 |
-
}
|
3135 |
}
|
3136 |
|
3137 |
-
|
3138 |
-
* @param $val
|
3139 |
-
*/
|
3140 |
-
function set_outline_width($val)
|
3141 |
{
|
3142 |
-
$
|
3143 |
}
|
3144 |
|
3145 |
-
|
3146 |
-
* @param $val
|
3147 |
-
*/
|
3148 |
-
function set_outline_color($val)
|
3149 |
{
|
3150 |
-
$this->
|
3151 |
}
|
3152 |
|
3153 |
/**
|
3154 |
-
* @
|
3155 |
*/
|
3156 |
-
function
|
3157 |
{
|
3158 |
-
$this->
|
3159 |
}
|
3160 |
|
3161 |
/**
|
3162 |
-
*
|
|
|
3163 |
*
|
3164 |
-
* @link
|
3165 |
-
* @param float $val
|
3166 |
*/
|
3167 |
-
function
|
3168 |
{
|
3169 |
-
$
|
3170 |
|
3171 |
-
if ($
|
3172 |
-
|
3173 |
-
return;
|
3174 |
}
|
3175 |
|
3176 |
-
$
|
|
|
|
|
|
|
3177 |
|
3178 |
-
if (
|
3179 |
-
|
3180 |
}
|
3181 |
|
3182 |
-
|
3183 |
}
|
3184 |
|
3185 |
/**
|
3186 |
-
*
|
3187 |
-
*
|
3188 |
-
* @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
|
3189 |
-
* @param $val
|
3190 |
*/
|
3191 |
-
function
|
3192 |
{
|
3193 |
-
$this->_prop_cache["list_style_image"] = null;
|
3194 |
-
|
3195 |
-
if ($val === "inherit") {
|
3196 |
-
$this->_props_computed["list_style_image"] = null;
|
3197 |
-
return;
|
3198 |
-
}
|
3199 |
-
|
3200 |
$parsed_val = $this->_stylesheet->resolve_url($val);
|
3201 |
|
3202 |
if ($parsed_val === "none") {
|
3203 |
-
|
3204 |
} else {
|
3205 |
-
|
3206 |
}
|
3207 |
}
|
3208 |
|
3209 |
/**
|
3210 |
-
*
|
3211 |
-
*
|
3212 |
-
* @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
|
3213 |
-
* @param string $value
|
3214 |
-
* @param bool $important
|
3215 |
*/
|
3216 |
-
function
|
3217 |
{
|
3218 |
static $positions = ["inside", "outside"];
|
3219 |
static $types = [
|
@@ -3229,14 +3346,15 @@ class Style
|
|
3229 |
];
|
3230 |
|
3231 |
$components = $this->parse_property_value($value);
|
|
|
3232 |
|
3233 |
foreach ($components as $val) {
|
3234 |
-
/*
|
3235 |
* A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
|
3236 |
*/
|
3237 |
if ($val === "none") {
|
3238 |
-
$
|
3239 |
-
$
|
3240 |
continue;
|
3241 |
}
|
3242 |
|
@@ -3246,70 +3364,87 @@ class Style
|
|
3246 |
//Internet Explorer 7/8 and dompdf is right.
|
3247 |
|
3248 |
if (mb_substr($val, 0, 4) === "url(") {
|
3249 |
-
$
|
3250 |
continue;
|
3251 |
}
|
3252 |
|
3253 |
-
if (in_array($val, $types, true)) {
|
3254 |
-
$
|
3255 |
-
} elseif (in_array($val, $positions, true)) {
|
3256 |
-
$
|
3257 |
}
|
3258 |
}
|
|
|
|
|
3259 |
}
|
3260 |
|
3261 |
/**
|
3262 |
-
* @
|
3263 |
*/
|
3264 |
-
function
|
3265 |
{
|
3266 |
-
$this->_prop_cache["size"] = null;
|
3267 |
-
|
3268 |
-
$length_re = "/(\d+\s*(?:pt|px|pc|rem|em|ex|in|cm|mm|%))/";
|
3269 |
-
|
3270 |
-
$val = mb_strtolower($val);
|
3271 |
-
|
3272 |
if ($val === "auto") {
|
3273 |
-
|
3274 |
-
return;
|
3275 |
}
|
3276 |
|
3277 |
-
$parts =
|
|
|
|
|
|
|
|
|
|
|
3278 |
|
3279 |
-
$
|
3280 |
-
|
3281 |
-
|
3282 |
|
3283 |
-
|
3284 |
-
|
|
|
|
|
|
|
3285 |
} else {
|
3286 |
-
$
|
3287 |
}
|
|
|
3288 |
|
3289 |
-
|
3290 |
-
|
3291 |
-
|
3292 |
-
} elseif (isset(CPDF::$PAPER_SIZES[$parts[0]])) {
|
3293 |
-
$computed = array_slice(CPDF::$PAPER_SIZES[$parts[0]], 2, 2);
|
3294 |
|
3295 |
-
|
3296 |
-
|
3297 |
-
|
|
|
|
|
|
|
|
|
3298 |
} else {
|
3299 |
-
|
3300 |
-
|
|
|
|
|
|
|
|
|
|
|
3301 |
}
|
3302 |
|
3303 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
3304 |
}
|
3305 |
|
3306 |
/**
|
3307 |
-
*
|
|
|
3308 |
*
|
3309 |
-
* @link
|
3310 |
-
* @return array|null
|
3311 |
*/
|
3312 |
-
function
|
3313 |
{
|
3314 |
//TODO: should be handled in setter (lengths set to absolute)
|
3315 |
|
@@ -3317,8 +3452,8 @@ class Style
|
|
3317 |
$tr_value = "\s*([^,\s]+)\s*";
|
3318 |
$angle = "\s*([^,\s]+(?:deg|rad)?)\s*";
|
3319 |
|
3320 |
-
if (!preg_match_all("/[a-z]+\([^\)]+\)/i", $
|
3321 |
-
return
|
3322 |
}
|
3323 |
|
3324 |
$functions = [
|
@@ -3346,7 +3481,7 @@ class Style
|
|
3346 |
|
3347 |
foreach ($functions as $name => $pattern) {
|
3348 |
if (preg_match("/$name\s*$pattern/i", $t, $matches)) {
|
3349 |
-
$values = array_slice($matches, 1);
|
3350 |
|
3351 |
switch ($name) {
|
3352 |
// <angle> units
|
@@ -3357,9 +3492,9 @@ class Style
|
|
3357 |
|
3358 |
foreach ($values as $i => $value) {
|
3359 |
if (strpos($value, "rad")) {
|
3360 |
-
$values[$i] = rad2deg(
|
3361 |
} else {
|
3362 |
-
$values[$i] =
|
3363 |
}
|
3364 |
}
|
3365 |
|
@@ -3431,54 +3566,21 @@ class Style
|
|
3431 |
}
|
3432 |
|
3433 |
/**
|
3434 |
-
* @param $
|
3435 |
-
|
3436 |
-
function set_transform($val)
|
3437 |
-
{
|
3438 |
-
$this->_prop_cache["transform"] = null;
|
3439 |
-
|
3440 |
-
if ($val === "inherit") {
|
3441 |
-
$this->_props_computed["transform"] = null;
|
3442 |
-
return;
|
3443 |
-
}
|
3444 |
-
|
3445 |
-
$this->_props_computed["transform"] = $val;
|
3446 |
-
}
|
3447 |
-
|
3448 |
-
/**
|
3449 |
-
* Sets the CSS3 transform-origin property
|
3450 |
-
*
|
3451 |
-
* @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
|
3452 |
-
* @param string $val
|
3453 |
-
*/
|
3454 |
-
function set_transform_origin($val)
|
3455 |
-
{
|
3456 |
-
$this->_prop_cache["transform_origin"] = null;
|
3457 |
-
|
3458 |
-
if ($val === "inherit") {
|
3459 |
-
$this->_props_computed["transform_origin"] = null;
|
3460 |
-
return;
|
3461 |
-
}
|
3462 |
-
|
3463 |
-
$this->_props_computed["transform_origin"] = $val;
|
3464 |
-
}
|
3465 |
-
|
3466 |
-
/**
|
3467 |
-
* Gets the CSS3 transform-origin property
|
3468 |
*
|
3469 |
-
* @link
|
3470 |
-
* @return mixed[]
|
3471 |
*/
|
3472 |
-
function
|
3473 |
{
|
3474 |
//TODO: should be handled in setter
|
3475 |
-
|
3476 |
-
$values = preg_split("/\s+/", $
|
3477 |
|
3478 |
$values = array_map(function ($value) {
|
3479 |
-
if (in_array($value, ["top", "left"])) {
|
3480 |
return 0;
|
3481 |
-
}
|
3482 |
return "100%";
|
3483 |
} else {
|
3484 |
return $value;
|
@@ -3493,10 +3595,10 @@ class Style
|
|
3493 |
}
|
3494 |
|
3495 |
/**
|
3496 |
-
* @param $val
|
3497 |
-
* @return null
|
3498 |
*/
|
3499 |
-
protected function parse_image_resolution($val)
|
3500 |
{
|
3501 |
// If exif data could be get:
|
3502 |
// $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';
|
@@ -3512,60 +3614,65 @@ class Style
|
|
3512 |
|
3513 |
/**
|
3514 |
* auto | normal | dpi
|
3515 |
-
*
|
3516 |
-
* @param $val
|
3517 |
*/
|
3518 |
-
function
|
3519 |
{
|
3520 |
-
$this->
|
|
|
3521 |
|
3522 |
-
|
3523 |
-
|
3524 |
-
|
3525 |
-
|
|
|
|
|
|
|
3526 |
|
3527 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
3528 |
|
3529 |
-
|
|
|
|
|
|
|
|
|
|
|
3530 |
}
|
3531 |
|
3532 |
/**
|
3533 |
-
*
|
3534 |
-
*
|
3535 |
-
* @param $val
|
3536 |
*/
|
3537 |
-
function
|
3538 |
{
|
3539 |
-
$
|
|
|
3540 |
|
3541 |
-
if ($val
|
3542 |
-
|
3543 |
-
return;
|
3544 |
}
|
3545 |
|
3546 |
-
$
|
|
|
|
|
3547 |
|
3548 |
-
|
3549 |
}
|
3550 |
|
3551 |
/**
|
3552 |
-
* @
|
3553 |
*/
|
3554 |
-
function
|
3555 |
{
|
3556 |
-
$
|
3557 |
-
|
3558 |
-
if ($val === "inherit") {
|
3559 |
-
$this->_props_computed["z_index"] = null;
|
3560 |
-
return;
|
3561 |
-
}
|
3562 |
-
|
3563 |
-
if ($val !== "auto" && round((float) $val) != $val) {
|
3564 |
-
$this->_props_computed["z_index"] = null;
|
3565 |
-
return;
|
3566 |
}
|
3567 |
|
3568 |
-
$this->
|
3569 |
}
|
3570 |
|
3571 |
/**
|
@@ -3595,18 +3702,18 @@ class Style
|
|
3595 |
* @return string
|
3596 |
*/
|
3597 |
/*DEBUGCSS print: see below additional debugging util*/
|
3598 |
-
function __toString()
|
3599 |
{
|
3600 |
$parent_font_size = $this->parent_style
|
3601 |
? $this->parent_style->font_size
|
3602 |
: self::$default_font_size;
|
3603 |
|
3604 |
-
return print_r(array_merge(["parent_font_size" => $parent_font_size
|
3605 |
$this->_props), true);
|
3606 |
}
|
3607 |
|
3608 |
/*DEBUGCSS*/
|
3609 |
-
function debug_print()
|
3610 |
{
|
3611 |
$parent_font_size = $this->parent_style
|
3612 |
? $this->parent_style->font_size
|
@@ -3630,7 +3737,7 @@ class Style
|
|
3630 |
}
|
3631 |
print " ]\n";
|
3632 |
print " cached [\n";
|
3633 |
-
foreach ($this->
|
3634 |
print ' ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true));
|
3635 |
print ";\n";
|
3636 |
}
|
22 |
* It includes methods to resolve colors and lengths, as well as getters &
|
23 |
* setters for many CSS properties.
|
24 |
*
|
25 |
+
* Access to the different CSS properties is provided by the methods
|
26 |
+
* {@link Style::set_prop()} and {@link Style::get_specified()}, and the
|
27 |
+
* property overload methods {@link Style::__set()} and {@link Style::__get()},
|
28 |
+
* as well as {@link Style::set_used()}. The latter methods operate on used
|
29 |
+
* values and permit access to any (CSS) property using the following syntax:
|
30 |
+
*
|
31 |
+
* ```
|
32 |
+
* $style->margin_top = 10.0;
|
33 |
+
* echo $style->margin_top; // Returns `10.0`
|
34 |
+
* ```
|
35 |
+
*
|
36 |
+
* To declare a property from a string, use {@link Style::set_prop()}:
|
37 |
+
*
|
38 |
+
* ```
|
39 |
+
* $style->set_prop("margin_top", "1em");
|
40 |
+
* echo $style->get_specified("margin_top"); // Returns `1em`
|
41 |
+
* echo $style->margin_top; // Returns `12.0`, assuming the default font size
|
42 |
+
* ```
|
43 |
+
*
|
44 |
* Actual CSS parsing is performed in the {@link Stylesheet} class.
|
45 |
*
|
46 |
+
* @property string $azimuth
|
47 |
+
* @property string $background_attachment
|
48 |
+
* @property array|string $background_color
|
49 |
+
* @property string $background_image Image URL or `none`
|
50 |
+
* @property string $background_image_resolution
|
51 |
+
* @property array $background_position
|
52 |
+
* @property string $background_repeat
|
53 |
+
* @property array|string $background_size `cover`, `contain`, or `[width, height]`, each being a length, percentage, or `auto`
|
54 |
+
* @property string $border_collapse
|
55 |
+
* @property string $border_color Only use for setting all sides to the same color
|
56 |
+
* @property float[] $border_spacing Pair of `[horizontal, vertical]` spacing
|
57 |
+
* @property string $border_style Only use for setting all sides to the same style
|
58 |
+
* @property array|string $border_top_color
|
59 |
+
* @property array|string $border_right_color
|
60 |
+
* @property array|string $border_bottom_color
|
61 |
+
* @property array|string $border_left_color
|
62 |
+
* @property string $border_top_style Valid border style
|
63 |
+
* @property string $border_right_style Valid border style
|
64 |
+
* @property string $border_bottom_style Valid border style
|
65 |
+
* @property string $border_left_style Valid border style
|
66 |
+
* @property float $border_top_width Length in pt
|
67 |
+
* @property float $border_right_width Length in pt
|
68 |
+
* @property float $border_bottom_width Length in pt
|
69 |
+
* @property float $border_left_width Length in pt
|
70 |
+
* @property string $border_width Only use for setting all sides to the same width
|
71 |
+
* @property float|string $border_bottom_left_radius Radius in pt or a percentage value
|
72 |
+
* @property float|string $border_bottom_right_radius Radius in pt or a percentage value
|
73 |
+
* @property float|string $border_top_left_radius Radius in pt or a percentage value
|
74 |
+
* @property float|string $border_top_right_radius Radius in pt or a percentage value
|
75 |
+
* @property string $border_radius Only use for setting all corners to the same radius
|
76 |
+
* @property float|string $bottom Length in pt, a percentage value, or `auto`
|
77 |
+
* @property string $caption_side
|
78 |
+
* @property string $clear
|
79 |
+
* @property string $clip
|
80 |
+
* @property array|string $color
|
81 |
+
* @property string[]|string $content List of content components, `normal`, or `none`
|
82 |
+
* @property array|string $counter_increment Array defining the counters to increment or `none`
|
83 |
+
* @property array|string $counter_reset Array defining the counters to reset or `none`
|
84 |
+
* @property string $cue_after
|
85 |
+
* @property string $cue_before
|
86 |
+
* @property string $cue
|
87 |
+
* @property string $cursor
|
88 |
+
* @property string $direction
|
89 |
+
* @property string $display
|
90 |
+
* @property string $elevation
|
91 |
+
* @property string $empty_cells
|
92 |
+
* @property string $float
|
93 |
+
* @property string $font_family
|
94 |
+
* @property float $font_size Length in pt
|
95 |
+
* @property string $font_style
|
96 |
+
* @property string $font_variant
|
97 |
+
* @property string $font_weight
|
98 |
+
* @property float|string $height Length in pt, a percentage value, or `auto`
|
99 |
+
* @property string $image_resolution
|
100 |
+
* @property string $inset Only use for setting all box insets to the same length
|
101 |
+
* @property float|string $left Length in pt, a percentage value, or `auto`
|
102 |
+
* @property float $letter_spacing Length in pt
|
103 |
+
* @property float $line_height Length in pt
|
104 |
+
* @property string $list_style_image Image URL or `none`
|
105 |
+
* @property string $list_style_position
|
106 |
+
* @property string $list_style_type
|
107 |
+
* @property float|string $margin_right Length in pt, a percentage value, or `auto`
|
108 |
+
* @property float|string $margin_left Length in pt, a percentage value, or `auto`
|
109 |
+
* @property float|string $margin_top Length in pt, a percentage value, or `auto`
|
110 |
+
* @property float|string $margin_bottom Length in pt, a percentage value, or `auto`
|
111 |
+
* @property string $margin Only use for setting all sides to the same length
|
112 |
+
* @property float|string $max_height Length in pt, a percentage value, or `none`
|
113 |
+
* @property float|string $max_width Length in pt, a percentage value, or `none`
|
114 |
+
* @property float|string $min_height Length in pt, a percentage value, or `auto`
|
115 |
+
* @property float|string $min_width Length in pt, a percentage value, or `auto`
|
116 |
+
* @property float $opacity Number in the range [0, 1]
|
117 |
+
* @property int $orphans
|
118 |
+
* @property array|string $outline_color
|
119 |
+
* @property string $outline_style Valid border style, except for `hidden`
|
120 |
+
* @property float $outline_width Length in pt
|
121 |
+
* @property float $outline_offset Length in pt
|
122 |
+
* @property string $overflow
|
123 |
+
* @property string $overflow_wrap
|
124 |
+
* @property float|string $padding_top Length in pt or a percentage value
|
125 |
+
* @property float|string $padding_right Length in pt or a percentage value
|
126 |
+
* @property float|string $padding_bottom Length in pt or a percentage value
|
127 |
+
* @property float|string $padding_left Length in pt or a percentage value
|
128 |
+
* @property string $padding Only use for setting all sides to the same length
|
129 |
+
* @property string $page_break_after
|
130 |
+
* @property string $page_break_before
|
131 |
+
* @property string $page_break_inside
|
132 |
+
* @property string $pause_after
|
133 |
+
* @property string $pause_before
|
134 |
+
* @property string $pause
|
135 |
+
* @property string $pitch_range
|
136 |
+
* @property string $pitch
|
137 |
+
* @property string $play_during
|
138 |
+
* @property string $position
|
139 |
+
* @property string $quotes
|
140 |
+
* @property string $richness
|
141 |
+
* @property float|string $right Length in pt, a percentage value, or `auto`
|
142 |
+
* @property float[]|string $size Pair of `[width, height]` or `auto`
|
143 |
+
* @property string $speak_header
|
144 |
+
* @property string $speak_numeral
|
145 |
+
* @property string $speak_punctuation
|
146 |
+
* @property string $speak
|
147 |
+
* @property string $speech_rate
|
148 |
+
* @property string $src
|
149 |
+
* @property string $stress
|
150 |
+
* @property string $table_layout
|
151 |
+
* @property string $text_align
|
152 |
+
* @property string $text_decoration
|
153 |
+
* @property float|string $text_indent Length in pt or a percentage value
|
154 |
+
* @property string $text_transform
|
155 |
+
* @property float|string $top Length in pt, a percentage value, or `auto`
|
156 |
+
* @property array $transform List of transforms
|
157 |
+
* @property array $transform_origin
|
158 |
+
* @property string $unicode_bidi
|
159 |
+
* @property string $unicode_range
|
160 |
+
* @property string $vertical_align
|
161 |
+
* @property string $visibility
|
162 |
+
* @property string $voice_family
|
163 |
+
* @property string $volume
|
164 |
+
* @property string $white_space
|
165 |
+
* @property int $widows
|
166 |
+
* @property float|string $width Length in pt, a percentage value, or `auto`
|
167 |
+
* @property string $word_break
|
168 |
+
* @property float $word_spacing Length in pt
|
169 |
+
* @property int|string $z_index Integer value or `auto`
|
170 |
+
* @property string $_dompdf_keep
|
171 |
+
*
|
172 |
* @package dompdf
|
173 |
*/
|
174 |
class Style
|
175 |
{
|
176 |
+
protected const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
|
177 |
+
protected const CSS_INTEGER = "[+-]?\d+";
|
178 |
+
protected const CSS_NUMBER = "[+-]?\d*\.?\d+(?:[eE][+-]?\d+)?";
|
179 |
|
180 |
/**
|
181 |
* Default font size, in points.
|
182 |
*
|
183 |
* @var float
|
184 |
*/
|
185 |
+
public static $default_font_size = 12;
|
186 |
|
187 |
/**
|
188 |
* Default line height, as a fraction of the font size.
|
189 |
*
|
190 |
* @var float
|
191 |
*/
|
192 |
+
public static $default_line_height = 1.2;
|
193 |
|
194 |
/**
|
195 |
* Default "absolute" font sizes relative to the default font-size
|
196 |
+
* https://www.w3.org/TR/css-fonts-3/#absolute-size-value
|
197 |
+
*
|
198 |
* @var array<float>
|
199 |
*/
|
200 |
+
public static $font_size_keywords = [
|
201 |
"xx-small" => 0.6, // 3/5
|
202 |
"x-small" => 0.75, // 3/4
|
203 |
"small" => 0.889, // 8/9
|
208 |
];
|
209 |
|
210 |
/**
|
211 |
+
* List of valid text-align keywords.
|
|
|
|
|
212 |
*/
|
213 |
+
public const TEXT_ALIGN_KEYWORDS = ["left", "right", "center", "justify"];
|
214 |
|
215 |
/**
|
216 |
+
* List of valid vertical-align keywords.
|
|
|
|
|
217 |
*/
|
218 |
+
public const VERTICAL_ALIGN_KEYWORDS = ["baseline", "bottom", "middle",
|
219 |
+
"sub", "super", "text-bottom", "text-top", "top"];
|
220 |
|
221 |
/**
|
222 |
* List of all block-level (outer) display types.
|
261 |
];
|
262 |
|
263 |
/**
|
264 |
+
* List of all inline (inner) display types.
|
|
|
|
|
265 |
*/
|
266 |
+
public const INLINE_TYPES = ["inline"];
|
267 |
|
268 |
/**
|
269 |
+
* List of all block (inner) display types.
|
|
|
|
|
270 |
*/
|
271 |
+
public const BLOCK_TYPES = ["block", "inline-block", "table-cell", "list-item"];
|
272 |
|
273 |
/**
|
274 |
+
* List of all table (inner) display types.
|
|
|
|
|
275 |
*/
|
276 |
+
public const TABLE_TYPES = ["table", "inline-table"];
|
277 |
|
278 |
/**
|
279 |
* Lookup table for valid display types. Initially computed from the
|
284 |
protected static $valid_display_types = [];
|
285 |
|
286 |
/**
|
287 |
+
* List of all positioned types.
|
288 |
+
*/
|
289 |
+
public const POSITIONED_TYPES = ["relative", "absolute", "fixed"];
|
290 |
+
|
291 |
+
/**
|
292 |
+
* List of valid border styles.
|
293 |
*/
|
294 |
+
public const BORDER_STYLES = [
|
295 |
+
"none", "hidden",
|
296 |
+
"dotted", "dashed", "solid",
|
297 |
+
"double", "groove", "ridge", "inset", "outset"
|
298 |
+
];
|
299 |
|
300 |
/**
|
301 |
+
* List of valid outline-style values.
|
302 |
+
* Same as the border styles, except `auto` is allowed, `hidden` is not.
|
303 |
*
|
304 |
+
* @link https://www.w3.org/TR/css-ui-4/#typedef-outline-line-style
|
305 |
*/
|
306 |
+
protected const OUTLINE_STYLES = [
|
307 |
+
"auto", "none",
|
308 |
+
"dotted", "dashed", "solid",
|
309 |
+
"double", "groove", "ridge", "inset", "outset"
|
310 |
+
];
|
311 |
|
312 |
/**
|
313 |
* Map of CSS shorthand properties and their corresponding sub-properties.
|
328 |
"background_color"
|
329 |
],
|
330 |
"border" => [
|
331 |
+
"border_top_width",
|
332 |
+
"border_right_width",
|
333 |
+
"border_bottom_width",
|
334 |
+
"border_left_width",
|
335 |
+
"border_top_style",
|
336 |
+
"border_right_style",
|
337 |
+
"border_bottom_style",
|
338 |
+
"border_left_style",
|
339 |
+
"border_top_color",
|
340 |
+
"border_right_color",
|
341 |
+
"border_bottom_color",
|
342 |
+
"border_left_color"
|
343 |
],
|
344 |
"border_top" => [
|
345 |
"border_top_width",
|
394 |
"font_weight",
|
395 |
"line_height"
|
396 |
],
|
397 |
+
"inset" => [
|
398 |
+
"top",
|
399 |
+
"right",
|
400 |
+
"bottom",
|
401 |
+
"left"
|
402 |
+
],
|
403 |
"list_style" => [
|
404 |
"list_style_image",
|
405 |
"list_style_position",
|
440 |
/**
|
441 |
* Default style values.
|
442 |
*
|
443 |
+
* @link https://www.w3.org/TR/CSS21/propidx.html
|
444 |
*
|
445 |
* @var array
|
446 |
*/
|
449 |
/**
|
450 |
* List of inherited properties
|
451 |
*
|
452 |
+
* @link https://www.w3.org/TR/CSS21/propidx.html
|
453 |
*
|
454 |
* @var array
|
455 |
*/
|
477 |
protected $_media_queries;
|
478 |
|
479 |
/**
|
480 |
+
* Properties set by an `!important` declaration.
|
|
|
481 |
*
|
482 |
* @var array
|
483 |
*/
|
484 |
+
protected $_important_props = [];
|
485 |
|
486 |
/**
|
487 |
+
* Specified (or declared) values of the CSS properties.
|
488 |
+
*
|
489 |
+
* https://www.w3.org/TR/css-cascade-3/#value-stages
|
490 |
*
|
491 |
* @var array
|
492 |
*/
|
493 |
+
protected $_props = [];
|
494 |
|
495 |
/**
|
496 |
* Computed values of the CSS properties.
|
504 |
*
|
505 |
* @var array
|
506 |
*/
|
507 |
+
protected $_props_used = [];
|
508 |
+
|
509 |
+
/**
|
510 |
+
* Marks properties with non-final used values that should be cleared on
|
511 |
+
* style reset.
|
512 |
+
*
|
513 |
+
* @var array
|
514 |
+
*/
|
515 |
+
protected $non_final_used = [];
|
516 |
|
517 |
protected static $_dependency_map = [
|
518 |
"border_top_style" => [
|
537 |
"border_right_width",
|
538 |
"border_bottom_width",
|
539 |
"border_left_width",
|
540 |
+
"border_top_left_radius",
|
541 |
+
"border_top_right_radius",
|
542 |
+
"border_bottom_right_radius",
|
543 |
+
"border_bottom_left_radius",
|
544 |
+
"letter_spacing",
|
545 |
"line_height",
|
546 |
"margin_top",
|
547 |
"margin_right",
|
552 |
"padding_top",
|
553 |
"padding_right",
|
554 |
"padding_bottom",
|
555 |
+
"padding_left",
|
556 |
+
"word_spacing",
|
557 |
+
"width",
|
558 |
+
"height",
|
559 |
+
"min-width",
|
560 |
+
"min-height",
|
561 |
+
"max-width",
|
562 |
+
"max-height"
|
563 |
],
|
564 |
"float" => [
|
565 |
"display"
|
588 |
protected $parent_style;
|
589 |
|
590 |
/**
|
591 |
+
* @var Frame|null
|
592 |
*/
|
593 |
protected $_frame;
|
594 |
|
599 |
*/
|
600 |
protected $_origin = Stylesheet::ORIG_AUTHOR;
|
601 |
|
|
|
602 |
/**
|
603 |
* The computed bottom spacing
|
604 |
+
*
|
605 |
+
* @var float|string|null
|
606 |
*/
|
607 |
private $_computed_bottom_spacing = null;
|
608 |
|
609 |
/**
|
610 |
+
* @var bool|null
|
611 |
*/
|
612 |
private $has_border_radius_cache = null;
|
613 |
|
614 |
/**
|
615 |
+
* @var array|null
|
616 |
*/
|
617 |
private $resolved_border_radius = null;
|
618 |
|
622 |
private $fontMetrics;
|
623 |
|
624 |
/**
|
625 |
+
* @param Stylesheet $stylesheet The stylesheet the style is associated with.
|
626 |
+
* @param int $origin
|
|
|
|
|
627 |
*/
|
628 |
+
public function __construct(Stylesheet $stylesheet, int $origin = Stylesheet::ORIG_AUTHOR)
|
629 |
{
|
630 |
+
$this->fontMetrics = $stylesheet->getFontMetrics();
|
631 |
|
632 |
$this->_stylesheet = $stylesheet;
|
633 |
$this->_media_queries = [];
|
640 |
$d =& self::$_defaults;
|
641 |
|
642 |
// All CSS 2.1 properties, and their default values
|
643 |
+
// Some properties are specified with their computed value for
|
644 |
+
// efficiency; this only works if the computed value is not
|
645 |
+
// dependent on another property
|
646 |
$d["azimuth"] = "center";
|
647 |
$d["background_attachment"] = "scroll";
|
648 |
$d["background_color"] = "transparent";
|
649 |
$d["background_image"] = "none";
|
650 |
$d["background_image_resolution"] = "normal";
|
651 |
+
$d["background_position"] = ["0%", "0%"];
|
652 |
$d["background_repeat"] = "repeat";
|
653 |
$d["background"] = "";
|
654 |
$d["border_collapse"] = "separate";
|
655 |
$d["border_color"] = "";
|
656 |
+
$d["border_spacing"] = [0.0, 0.0];
|
657 |
$d["border_style"] = "";
|
658 |
$d["border_top"] = "";
|
659 |
$d["border_right"] = "";
|
672 |
$d["border_bottom_width"] = "medium";
|
673 |
$d["border_left_width"] = "medium";
|
674 |
$d["border_width"] = "";
|
675 |
+
$d["border_bottom_left_radius"] = 0.0;
|
676 |
+
$d["border_bottom_right_radius"] = 0.0;
|
677 |
+
$d["border_top_left_radius"] = 0.0;
|
678 |
+
$d["border_top_right_radius"] = 0.0;
|
679 |
$d["border_radius"] = "";
|
680 |
$d["border"] = "";
|
681 |
$d["bottom"] = "auto";
|
703 |
$d["font"] = "";
|
704 |
$d["height"] = "auto";
|
705 |
$d["image_resolution"] = "normal";
|
706 |
+
$d["inset"] = "";
|
707 |
$d["left"] = "auto";
|
708 |
$d["letter_spacing"] = "normal";
|
709 |
$d["line_height"] = "normal";
|
711 |
$d["list_style_position"] = "outside";
|
712 |
$d["list_style_type"] = "disc";
|
713 |
$d["list_style"] = "";
|
714 |
+
$d["margin_right"] = 0.0;
|
715 |
+
$d["margin_left"] = 0.0;
|
716 |
+
$d["margin_top"] = 0.0;
|
717 |
+
$d["margin_bottom"] = 0.0;
|
718 |
$d["margin"] = "";
|
719 |
$d["max_height"] = "none";
|
720 |
$d["max_width"] = "none";
|
721 |
$d["min_height"] = "auto";
|
722 |
$d["min_width"] = "auto";
|
723 |
+
$d["orphans"] = 2;
|
724 |
$d["outline_color"] = "currentcolor"; // "invert" special color is not supported
|
725 |
$d["outline_style"] = "none";
|
726 |
$d["outline_width"] = "medium";
|
727 |
+
$d["outline_offset"] = 0.0;
|
728 |
$d["outline"] = "";
|
729 |
$d["overflow"] = "visible";
|
730 |
$d["overflow_wrap"] = "normal";
|
731 |
+
$d["padding_top"] = 0.0;
|
732 |
+
$d["padding_right"] = 0.0;
|
733 |
+
$d["padding_bottom"] = 0.0;
|
734 |
+
$d["padding_left"] = 0.0;
|
735 |
$d["padding"] = "";
|
736 |
$d["page_break_after"] = "auto";
|
737 |
$d["page_break_before"] = "auto";
|
756 |
$d["table_layout"] = "auto";
|
757 |
$d["text_align"] = "";
|
758 |
$d["text_decoration"] = "none";
|
759 |
+
$d["text_indent"] = 0.0;
|
760 |
$d["text_transform"] = "none";
|
761 |
$d["top"] = "auto";
|
762 |
$d["unicode_bidi"] = "normal";
|
765 |
$d["voice_family"] = "";
|
766 |
$d["volume"] = "medium";
|
767 |
$d["white_space"] = "normal";
|
768 |
+
$d["widows"] = 2;
|
769 |
$d["width"] = "auto";
|
770 |
+
$d["word_break"] = "normal";
|
771 |
$d["word_spacing"] = "normal";
|
772 |
$d["z_index"] = "auto";
|
773 |
|
774 |
// CSS3
|
775 |
+
$d["opacity"] = 1.0;
|
776 |
+
$d["background_size"] = ["auto", "auto"];
|
777 |
$d["transform"] = "none";
|
778 |
$d["transform_origin"] = "50% 50%";
|
779 |
|
829 |
"volume",
|
830 |
"white_space",
|
831 |
"widows",
|
832 |
+
"word_break",
|
833 |
"word_spacing",
|
834 |
];
|
835 |
|
861 |
}
|
862 |
|
863 |
/**
|
864 |
+
* Clear all non-final used values.
|
865 |
+
*
|
866 |
+
* @return void
|
867 |
*/
|
868 |
+
public function reset(): void
|
869 |
{
|
870 |
+
foreach (array_keys($this->non_final_used) as $prop) {
|
871 |
+
unset($this->_props_used[$prop]);
|
872 |
+
}
|
873 |
+
|
874 |
+
$this->non_final_used = [];
|
875 |
}
|
876 |
|
877 |
/**
|
878 |
+
* @param array $media_queries
|
879 |
*/
|
880 |
+
public function set_media_queries(array $media_queries): void
|
881 |
{
|
882 |
$this->_media_queries = $media_queries;
|
883 |
}
|
884 |
|
885 |
/**
|
886 |
+
* @return array
|
887 |
*/
|
888 |
+
public function get_media_queries(): array
|
889 |
{
|
890 |
return $this->_media_queries;
|
891 |
}
|
893 |
/**
|
894 |
* @param Frame $frame
|
895 |
*/
|
896 |
+
public function set_frame(Frame $frame): void
|
897 |
{
|
898 |
$this->_frame = $frame;
|
899 |
}
|
900 |
|
901 |
/**
|
902 |
+
* @return Frame|null
|
903 |
*/
|
904 |
+
public function get_frame(): ?Frame
|
905 |
{
|
906 |
return $this->_frame;
|
907 |
}
|
908 |
|
909 |
/**
|
910 |
+
* @param int $origin
|
911 |
*/
|
912 |
+
public function set_origin(int $origin): void
|
913 |
{
|
914 |
$this->_origin = $origin;
|
915 |
}
|
917 |
/**
|
918 |
* @return int
|
919 |
*/
|
920 |
+
public function get_origin(): int
|
921 |
{
|
922 |
return $this->_origin;
|
923 |
}
|
924 |
|
925 |
/**
|
926 |
+
* Returns the {@link Stylesheet} the style is associated with.
|
927 |
*
|
928 |
* @return Stylesheet
|
929 |
*/
|
930 |
+
public function get_stylesheet(): Stylesheet
|
931 |
{
|
932 |
return $this->_stylesheet;
|
933 |
}
|
959 |
*
|
960 |
* @return float|string
|
961 |
*/
|
962 |
+
public function length_in_pt($length, ?float $ref_size = null)
|
963 |
{
|
964 |
$font_size = $this->__get("font_size");
|
965 |
$ref_size = $ref_size ?? $font_size;
|
966 |
|
967 |
+
if (!\is_array($length)) {
|
968 |
$length = [$length];
|
969 |
}
|
970 |
|
982 |
}
|
983 |
|
984 |
$val = $this->single_length_in_pt((string) $l, $ref_size, $font_size);
|
985 |
+
$ret += $val ?? 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
986 |
}
|
987 |
|
988 |
return $ret;
|
1005 |
|
1006 |
$key = "$l/$ref_size/$font_size";
|
1007 |
|
1008 |
+
if (\array_key_exists($key, $cache)) {
|
1009 |
return $cache[$key];
|
1010 |
}
|
1011 |
|
1012 |
+
$number = self::CSS_NUMBER;
|
1013 |
+
$pattern = "/^($number)(.*)?$/";
|
1014 |
+
|
1015 |
+
if (!preg_match($pattern, $l, $matches)) {
|
1016 |
+
return null;
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
$v = (float) $matches[1];
|
1020 |
+
$unit = mb_strtolower($matches[2]);
|
1021 |
+
|
1022 |
+
if ($unit === "") {
|
1023 |
// Legacy support for unitless values, not covered by spec. Might
|
1024 |
// want to restrict this to unitless `0` in the future
|
1025 |
+
$value = $v;
|
1026 |
}
|
1027 |
|
1028 |
+
elseif ($unit === "%") {
|
1029 |
+
$value = $v / 100 * $ref_size;
|
1030 |
}
|
1031 |
|
1032 |
+
elseif ($unit === "px") {
|
1033 |
$dpi = $this->_stylesheet->get_dompdf()->getOptions()->getDpi();
|
1034 |
+
$value = ($v * 72) / $dpi;
|
1035 |
}
|
1036 |
|
1037 |
+
elseif ($unit === "pt") {
|
1038 |
+
$value = $v;
|
1039 |
}
|
1040 |
|
1041 |
+
elseif ($unit === "rem") {
|
1042 |
+
$tree = $this->_stylesheet->get_dompdf()->getTree();
|
1043 |
+
$root_style = $tree !== null ? $tree->get_root()->get_style() : null;
|
1044 |
$root_font_size = $root_style === null || $root_style === $this
|
1045 |
? $font_size
|
1046 |
+
: $root_style->__get("font_size");
|
1047 |
+
$value = $v * $root_font_size;
|
1048 |
+
|
1049 |
+
// Skip caching if the root style is not available yet, as to avoid
|
1050 |
+
// incorrectly cached values if the root font size is different from
|
1051 |
+
// the default
|
1052 |
+
if ($root_style === null) {
|
1053 |
+
return $value;
|
1054 |
+
}
|
1055 |
}
|
1056 |
|
1057 |
+
elseif ($unit === "em") {
|
1058 |
+
$value = $v * $font_size;
|
1059 |
}
|
1060 |
|
1061 |
+
elseif ($unit === "cm") {
|
1062 |
+
$value = $v * 72 / 2.54;
|
1063 |
}
|
1064 |
|
1065 |
+
elseif ($unit === "mm") {
|
1066 |
+
$value = $v * 72 / 25.4;
|
1067 |
}
|
1068 |
|
1069 |
+
elseif ($unit === "ex") {
|
1070 |
// FIXME: em:ex ratio?
|
1071 |
+
$value = $v * $font_size / 2;
|
1072 |
}
|
1073 |
|
1074 |
+
elseif ($unit === "in") {
|
1075 |
+
$value = $v * 72;
|
1076 |
}
|
1077 |
|
1078 |
+
elseif ($unit === "pc") {
|
1079 |
+
$value = $v * 12;
|
1080 |
}
|
1081 |
|
1082 |
else {
|
1087 |
return $cache[$key] = $value;
|
1088 |
}
|
1089 |
|
|
|
1090 |
/**
|
1091 |
* Resolve inherited property values using the provided parent style or the
|
1092 |
* default values, in case no parent style exists.
|
1094 |
* https://www.w3.org/TR/css-cascade-3/#inheriting
|
1095 |
*
|
1096 |
* @param Style|null $parent
|
|
|
|
|
1097 |
*/
|
1098 |
+
public function inherit(?Style $parent = null): void
|
1099 |
{
|
1100 |
$this->parent_style = $parent;
|
1101 |
|
1102 |
// Clear the computed font size, as it might depend on the parent
|
1103 |
// font size
|
1104 |
unset($this->_props_computed["font_size"]);
|
1105 |
+
unset($this->_props_used["font_size"]);
|
1106 |
|
1107 |
if ($parent) {
|
1108 |
foreach (self::$_inherited as $prop) {
|
1112 |
if (isset($this->_props[$prop]) || isset(self::$_props_shorthand[$prop])) {
|
1113 |
continue;
|
1114 |
}
|
1115 |
+
|
1116 |
if (isset($parent->_props[$prop])) {
|
1117 |
+
$parent_val = $parent->computed($prop);
|
1118 |
+
|
|
|
|
|
1119 |
$this->_props[$prop] = $parent_val;
|
1120 |
$this->_props_computed[$prop] = $parent_val;
|
1121 |
+
$this->_props_used[$prop] = null;
|
1122 |
}
|
1123 |
}
|
1124 |
}
|
1126 |
foreach ($this->_props as $prop => $val) {
|
1127 |
if ($val === "inherit") {
|
1128 |
if ($parent && isset($parent->_props[$prop])) {
|
1129 |
+
$parent_val = $parent->computed($prop);
|
|
|
|
|
1130 |
|
1131 |
$this->_props[$prop] = $parent_val;
|
1132 |
$this->_props_computed[$prop] = $parent_val;
|
1133 |
+
$this->_props_used[$prop] = null;
|
1134 |
} else {
|
1135 |
// Parent prop not set, use default
|
1136 |
$this->_props[$prop] = self::$_defaults[$prop];
|
1137 |
unset($this->_props_computed[$prop]);
|
1138 |
+
unset($this->_props_used[$prop]);
|
1139 |
}
|
1140 |
}
|
1141 |
}
|
|
|
|
|
1142 |
}
|
1143 |
|
1144 |
/**
|
1146 |
*
|
1147 |
* @param Style $style
|
1148 |
*/
|
1149 |
+
public function merge(Style $style): void
|
1150 |
{
|
1151 |
foreach ($style->_props as $prop => $val) {
|
1152 |
$important = isset($style->_important_props[$prop]);
|
1156 |
continue;
|
1157 |
}
|
1158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1159 |
if ($important) {
|
1160 |
$this->_important_props[$prop] = true;
|
1161 |
}
|
1162 |
|
1163 |
$this->_props[$prop] = $val;
|
1164 |
|
1165 |
+
// Copy an existing computed value only for non-dependent
|
1166 |
+
// properties; otherwise it may be invalid for the current style
|
1167 |
+
if (!isset(self::$_dependent_props[$prop])
|
1168 |
+
&& \array_key_exists($prop, $style->_props_computed)
|
1169 |
+
) {
|
1170 |
+
$this->_props_computed[$prop] = $style->_props_computed[$prop];
|
1171 |
+
$this->_props_used[$prop] = null;
|
1172 |
} else {
|
1173 |
+
unset($this->_props_computed[$prop]);
|
1174 |
+
unset($this->_props_used[$prop]);
|
1175 |
}
|
1176 |
}
|
1177 |
}
|
1178 |
|
1179 |
/**
|
1180 |
+
* Clear information about important declarations after the style has been
|
1181 |
+
* finalized during stylesheet loading.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1182 |
*/
|
1183 |
+
public function clear_important(): void
|
1184 |
{
|
1185 |
+
$this->_important_props = [];
|
|
|
1186 |
}
|
1187 |
|
1188 |
/**
|
1189 |
+
* Clear border-radius and bottom-spacing cache as necessary when a given
|
1190 |
+
* property is set.
|
1191 |
+
*
|
1192 |
+
* @param string $prop The property that is set.
|
1193 |
*/
|
1194 |
+
protected function clear_cache(string $prop): void
|
1195 |
{
|
1196 |
+
// Clear border-radius cache on setting any border-radius
|
1197 |
+
// property
|
1198 |
+
if ($prop === "border_top_left_radius"
|
1199 |
+
|| $prop === "border_top_right_radius"
|
1200 |
+
|| $prop === "border_bottom_left_radius"
|
1201 |
+
|| $prop === "border_bottom_right_radius"
|
1202 |
+
) {
|
1203 |
+
$this->has_border_radius_cache = null;
|
1204 |
+
$this->resolved_border_radius = null;
|
1205 |
+
}
|
1206 |
|
1207 |
+
// Clear bottom-spacing cache if necessary. Border style can
|
1208 |
+
// disable/enable border calculations
|
1209 |
+
if ($prop === "margin_bottom"
|
1210 |
+
|| $prop === "padding_bottom"
|
1211 |
+
|| $prop === "border_bottom_width"
|
1212 |
+
|| $prop === "border_bottom_style"
|
1213 |
+
) {
|
1214 |
+
$this->_computed_bottom_spacing = null;
|
1215 |
+
}
|
1216 |
}
|
1217 |
|
1218 |
/**
|
1219 |
+
* Set a style property from a value declaration.
|
1220 |
*
|
1221 |
* Setting `$clear_dependencies` to `false` is useful for saving a bit of
|
1222 |
* unnecessary work while loading stylesheets.
|
1223 |
*
|
1224 |
* @param string $prop The property to set.
|
1225 |
+
* @param mixed $val The value declaration or computed value.
|
1226 |
* @param bool $important Whether the declaration is important.
|
1227 |
* @param bool $clear_dependencies Whether to clear computed values of dependent properties.
|
1228 |
*/
|
1229 |
+
public function set_prop(string $prop, $val, bool $important = false, bool $clear_dependencies = true): void
|
1230 |
{
|
1231 |
$prop = str_replace("-", "_", $prop);
|
1232 |
|
1241 |
return;
|
1242 |
}
|
1243 |
|
1244 |
+
if ($prop !== "content" && \is_string($val) && mb_strpos($val, "url") === false && mb_strlen($val) > 1) {
|
1245 |
$val = mb_strtolower(trim(str_replace(["\n", "\t"], [" "], $val)));
|
|
|
1246 |
}
|
1247 |
|
1248 |
if (isset(self::$_props_shorthand[$prop])) {
|
1253 |
$this->set_prop($sub_prop, $val, $important, $clear_dependencies);
|
1254 |
}
|
1255 |
} else {
|
1256 |
+
$method = "_set_$prop";
|
1257 |
+
|
1258 |
if (!isset(self::$_methods_cache[$method])) {
|
1259 |
self::$_methods_cache[$method] = method_exists($this, $method);
|
1260 |
}
|
1261 |
+
|
1262 |
if (self::$_methods_cache[$method]) {
|
1263 |
+
$values = $this->$method($val);
|
1264 |
+
|
1265 |
+
if ($values === []) {
|
1266 |
+
return;
|
1267 |
+
}
|
1268 |
+
|
1269 |
+
// Each missing sub-property is assigned its initial value
|
1270 |
+
// https://www.w3.org/TR/css-cascade-3/#shorthand
|
1271 |
+
foreach (self::$_props_shorthand[$prop] as $sub_prop) {
|
1272 |
+
$sub_val = $values[$sub_prop] ?? self::$_defaults[$sub_prop];
|
1273 |
+
$this->set_prop($sub_prop, $sub_val, $important, $clear_dependencies);
|
1274 |
+
}
|
1275 |
}
|
1276 |
}
|
1277 |
} else {
|
1278 |
+
// Legacy support for `word-break: break-word`
|
1279 |
+
// https://www.w3.org/TR/css-text-3/#valdef-word-break-break-word
|
1280 |
+
if ($prop === "word_break" && $val === "break-word") {
|
1281 |
+
$val = "normal";
|
1282 |
+
$this->set_prop("overflow_wrap", "anywhere", $important, $clear_dependencies);
|
1283 |
+
}
|
1284 |
+
|
1285 |
// `!important` declarations take precedence over normal ones
|
1286 |
if (!$important && isset($this->_important_props[$prop])) {
|
1287 |
return;
|
1293 |
|
1294 |
// https://www.w3.org/TR/css-cascade-3/#inherit-initial
|
1295 |
if ($val === "unset") {
|
1296 |
+
$val = \in_array($prop, self::$_inherited, true)
|
1297 |
? "inherit"
|
1298 |
: "initial";
|
1299 |
}
|
1303 |
$val = self::$_defaults[$prop];
|
1304 |
}
|
1305 |
|
1306 |
+
$computed = $this->compute_prop($prop, $val);
|
1307 |
+
|
1308 |
+
// Skip invalid declarations
|
1309 |
+
if ($computed === null) {
|
1310 |
+
return;
|
1311 |
+
}
|
1312 |
+
|
1313 |
$this->_props[$prop] = $val;
|
1314 |
+
$this->_props_computed[$prop] = $computed;
|
1315 |
+
$this->_props_used[$prop] = null;
|
1316 |
|
1317 |
if ($clear_dependencies) {
|
1318 |
// Clear the computed values of any dependent properties, so
|
1320 |
if (isset(self::$_dependency_map[$prop])) {
|
1321 |
foreach (self::$_dependency_map[$prop] as $dependent) {
|
1322 |
unset($this->_props_computed[$dependent]);
|
1323 |
+
unset($this->_props_used[$dependent]);
|
1324 |
}
|
1325 |
}
|
1326 |
|
1327 |
+
$this->clear_cache($prop);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1328 |
}
|
1329 |
}
|
1330 |
}
|
1331 |
|
1332 |
/**
|
1333 |
+
* Get the specified value of a style property.
|
|
|
1334 |
*
|
1335 |
* @param string $prop
|
1336 |
*
|
1337 |
* @return mixed
|
1338 |
* @throws Exception
|
1339 |
*/
|
1340 |
+
public function get_specified(string $prop)
|
1341 |
{
|
1342 |
// Legacy property aliases
|
1343 |
if (isset(self::$_props_alias[$prop])) {
|
1348 |
throw new Exception("'$prop' is not a recognized CSS property.");
|
1349 |
}
|
1350 |
|
1351 |
+
return $this->_props[$prop] ?? self::$_defaults[$prop];
|
1352 |
+
}
|
1353 |
|
1354 |
+
/**
|
1355 |
+
* Set a style property to its final value.
|
1356 |
+
*
|
1357 |
+
* This sets the specified and used value of the style property to the given
|
1358 |
+
* value, meaning the value is not parsed and thus should have a type
|
1359 |
+
* compatible with the property.
|
1360 |
+
*
|
1361 |
+
* If a shorthand property is specified, all of its sub-properties are set
|
1362 |
+
* to the given value.
|
1363 |
+
*
|
1364 |
+
* @param string $prop The property to set.
|
1365 |
+
* @param mixed $val The final value of the property.
|
1366 |
+
*
|
1367 |
+
* @throws Exception
|
1368 |
+
*/
|
1369 |
+
public function __set(string $prop, $val)
|
1370 |
+
{
|
1371 |
+
// Legacy property aliases
|
1372 |
+
if (isset(self::$_props_alias[$prop])) {
|
1373 |
+
$prop = self::$_props_alias[$prop];
|
1374 |
+
}
|
1375 |
|
1376 |
+
if (!isset(self::$_defaults[$prop])) {
|
1377 |
+
throw new Exception("'$prop' is not a recognized CSS property.");
|
1378 |
}
|
1379 |
|
1380 |
+
if (isset(self::$_props_shorthand[$prop])) {
|
1381 |
+
foreach (self::$_props_shorthand[$prop] as $sub_prop) {
|
1382 |
+
$this->__set($sub_prop, $val);
|
1383 |
+
}
|
1384 |
+
} else {
|
1385 |
+
$this->_props[$prop] = $val;
|
1386 |
+
$this->_props_computed[$prop] = $val;
|
1387 |
+
$this->_props_used[$prop] = $val;
|
1388 |
+
|
1389 |
+
$this->clear_cache($prop);
|
1390 |
+
}
|
1391 |
}
|
1392 |
|
1393 |
/**
|
1394 |
+
* Set the used value of a style property.
|
1395 |
*
|
1396 |
+
* Used values are cleared on style reset.
|
|
|
|
|
|
|
|
|
|
|
1397 |
*
|
1398 |
+
* If a shorthand property is specified, all of its sub-properties are set
|
1399 |
+
* to the given value.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1400 |
*
|
1401 |
+
* @param string $prop The property to set.
|
1402 |
+
* @param mixed $val The used value of the property.
|
1403 |
*
|
1404 |
+
* @throws Exception
|
1405 |
*/
|
1406 |
+
public function set_used(string $prop, $val): void
|
1407 |
{
|
1408 |
+
// Legacy property aliases
|
1409 |
+
if (isset(self::$_props_alias[$prop])) {
|
1410 |
+
$prop = self::$_props_alias[$prop];
|
1411 |
+
}
|
1412 |
+
|
1413 |
+
if (!isset(self::$_defaults[$prop])) {
|
1414 |
+
throw new Exception("'$prop' is not a recognized CSS property.");
|
1415 |
+
}
|
1416 |
+
|
1417 |
+
if (isset(self::$_props_shorthand[$prop])) {
|
1418 |
+
foreach (self::$_props_shorthand[$prop] as $sub_prop) {
|
1419 |
+
$this->set_used($sub_prop, $val);
|
1420 |
+
}
|
1421 |
+
} else {
|
1422 |
+
$this->_props_used[$prop] = $val;
|
1423 |
+
$this->non_final_used[$prop] = true;
|
1424 |
+
}
|
1425 |
}
|
1426 |
|
1427 |
/**
|
1428 |
+
* Get the used or computed value of a style property, depending on whether
|
1429 |
+
* the used value has been determined yet.
|
|
|
|
|
|
|
|
|
1430 |
*
|
1431 |
* @param string $prop
|
1432 |
*
|
1433 |
* @return mixed
|
1434 |
* @throws Exception
|
1435 |
*/
|
1436 |
+
public function __get(string $prop)
|
1437 |
{
|
1438 |
// Legacy property aliases
|
1439 |
if (isset(self::$_props_alias[$prop])) {
|
1444 |
throw new Exception("'$prop' is not a recognized CSS property.");
|
1445 |
}
|
1446 |
|
1447 |
+
if (isset($this->_props_used[$prop])) {
|
1448 |
+
return $this->_props_used[$prop];
|
1449 |
}
|
1450 |
|
1451 |
+
$method = "_get_$prop";
|
1452 |
+
|
1453 |
if (!isset(self::$_methods_cache[$method])) {
|
1454 |
self::$_methods_cache[$method] = method_exists($this, $method);
|
1455 |
}
|
1463 |
} else {
|
1464 |
return implode(" ", array_map(function ($sub_prop) {
|
1465 |
$val = $this->__get($sub_prop);
|
1466 |
+
return \is_array($val) ? implode(" ", $val) : $val;
|
1467 |
}, self::$_props_shorthand[$prop]));
|
1468 |
}
|
1469 |
} else {
|
1470 |
+
$computed = $this->computed($prop);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1471 |
$used = self::$_methods_cache[$method]
|
1472 |
+
? $this->$method($computed)
|
1473 |
: $computed;
|
1474 |
|
1475 |
+
$this->_props_used[$prop] = $used;
|
1476 |
return $used;
|
1477 |
}
|
1478 |
}
|
1479 |
|
1480 |
/**
|
1481 |
+
* @param string $prop The property to compute.
|
1482 |
+
* @param mixed $val The value to compute. Non-string values are treated as already computed.
|
|
|
|
|
1483 |
*
|
1484 |
+
* @return mixed The computed value.
|
|
|
1485 |
*/
|
1486 |
+
protected function compute_prop(string $prop, $val)
|
1487 |
{
|
1488 |
+
// During style merge, the parent style is not available yet, so
|
1489 |
+
// temporarily use the initial value for `inherit` properties. The
|
1490 |
+
// keyword is properly resolved during inheritance
|
1491 |
+
if ($val === "inherit") {
|
1492 |
+
$val = self::$_defaults[$prop];
|
1493 |
}
|
1494 |
|
1495 |
+
// Check for values which are already computed
|
1496 |
+
if (!\is_string($val)) {
|
1497 |
+
return $val;
|
1498 |
}
|
1499 |
|
1500 |
+
$method = "_compute_$prop";
|
1501 |
+
|
1502 |
+
if (!isset(self::$_methods_cache[$method])) {
|
1503 |
+
self::$_methods_cache[$method] = method_exists($this, $method);
|
1504 |
+
}
|
1505 |
+
|
1506 |
+
if (self::$_methods_cache[$method]) {
|
1507 |
+
return $this->$method($val);
|
1508 |
+
} elseif ($val !== "") {
|
1509 |
+
return $val;
|
1510 |
} else {
|
1511 |
+
return null;
|
1512 |
}
|
1513 |
}
|
1514 |
|
1515 |
/**
|
1516 |
+
* Get the computed value for the given property.
|
1517 |
+
*
|
1518 |
+
* @param string $prop The property to get the computed value of.
|
1519 |
*
|
1520 |
* @return mixed The computed value.
|
1521 |
*/
|
1522 |
+
protected function computed(string $prop)
|
1523 |
{
|
1524 |
+
if (!\array_key_exists($prop, $this->_props_computed)) {
|
1525 |
+
$val = $this->_props[$prop] ?? self::$_defaults[$prop];
|
1526 |
+
$computed = $this->compute_prop($prop, $val);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1527 |
|
1528 |
+
$this->_props_computed[$prop] = $computed;
|
|
|
|
|
|
|
1529 |
}
|
1530 |
|
1531 |
return $this->_props_computed[$prop];
|
1533 |
|
1534 |
/**
|
1535 |
* @param float $cbw The width of the containing block.
|
1536 |
+
* @return float|string|null
|
1537 |
*/
|
1538 |
+
public function computed_bottom_spacing(float $cbw)
|
1539 |
{
|
1540 |
// Caching the bottom spacing independently of the given width is a bit
|
1541 |
// iffy, but should be okay, as the containing block should only
|
1554 |
);
|
1555 |
}
|
1556 |
|
1557 |
+
/**
|
1558 |
+
* Returns an `array(r, g, b, "r" => r, "g" => g, "b" => b, "alpha" => alpha, "hex" => "#rrggbb")`
|
1559 |
+
* based on the provided CSS color value.
|
1560 |
+
*
|
1561 |
+
* @param string|null $color
|
1562 |
+
* @return array|string|null
|
1563 |
+
*/
|
1564 |
+
public function munge_color($color)
|
1565 |
+
{
|
1566 |
+
return Color::parse($color);
|
1567 |
+
}
|
1568 |
+
|
1569 |
/**
|
1570 |
* @return string
|
1571 |
*/
|
1572 |
+
public function get_font_family_raw(): string
|
1573 |
{
|
1574 |
return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
|
1575 |
}
|
1576 |
|
1577 |
/**
|
1578 |
+
* Getter for the `font-family` CSS property.
|
1579 |
+
*
|
1580 |
* Uses the {@link FontMetrics} class to resolve the font family into an
|
1581 |
* actual font file.
|
1582 |
*
|
1583 |
+
* @param string $computed
|
1584 |
+
* @return string
|
1585 |
* @throws Exception
|
1586 |
*
|
1587 |
+
* @link https://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
|
1588 |
*/
|
1589 |
+
protected function _get_font_family($computed): string
|
1590 |
{
|
1591 |
//TODO: we should be using the calculated prop rather than perform the entire family parsing operation again
|
1592 |
|
1593 |
+
$fontMetrics = $this->getFontMetrics();
|
1594 |
$DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss();
|
1595 |
|
1596 |
// Select the appropriate font. First determine the subtype, then check
|
1608 |
|
1609 |
// Resolve font-style
|
1610 |
$font_style = $this->__get("font_style");
|
1611 |
+
$subtype = $fontMetrics->getType($weight . ' ' . $font_style);
|
1612 |
|
1613 |
+
$families = preg_split("/\s*,\s*/", $computed);
|
1614 |
|
1615 |
$font = null;
|
1616 |
foreach ($families as $family) {
|
1620 |
if ($DEBUGCSS) {
|
1621 |
print '(' . $family . ')';
|
1622 |
}
|
1623 |
+
$font = $fontMetrics->getFont($family, $subtype);
|
1624 |
|
1625 |
if ($font) {
|
1626 |
if ($DEBUGCSS) {
|
1627 |
print "<pre>[get_font_family:";
|
1628 |
+
print '(' . $computed . '.' . $font_style . '.' . $weight . '.' . $subtype . ')';
|
1629 |
print '(' . $font . ")get_font_family]\n</pre>";
|
1630 |
}
|
1631 |
return $font;
|
1636 |
if ($DEBUGCSS) {
|
1637 |
print '(default)';
|
1638 |
}
|
1639 |
+
$font = $fontMetrics->getFont($family, $subtype);
|
1640 |
|
1641 |
if ($font) {
|
1642 |
if ($DEBUGCSS) {
|
1645 |
return $font;
|
1646 |
}
|
1647 |
|
1648 |
+
throw new Exception("Unable to find a suitable font replacement for: '" . $computed . "'");
|
1649 |
}
|
1650 |
|
1651 |
/**
|
1652 |
+
* @param float|string $computed
|
1653 |
* @return float
|
1654 |
+
*
|
1655 |
+
* @link https://www.w3.org/TR/css-text-4/#word-spacing-property
|
1656 |
*/
|
1657 |
+
protected function _get_word_spacing($computed)
|
1658 |
{
|
1659 |
+
if (\is_float($computed)) {
|
1660 |
+
return $computed;
|
|
|
|
|
|
|
|
|
|
|
|
|
1661 |
}
|
1662 |
|
1663 |
+
// Resolve percentage values
|
1664 |
+
$font_size = $this->__get("font_size");
|
1665 |
+
return $this->single_length_in_pt($computed, $font_size);
|
1666 |
}
|
1667 |
|
1668 |
/**
|
1669 |
+
* @param float|string $computed
|
1670 |
* @return float
|
1671 |
+
*
|
1672 |
+
* @link https://www.w3.org/TR/css-text-4/#letter-spacing-property
|
1673 |
*/
|
1674 |
+
protected function _get_letter_spacing($computed)
|
1675 |
{
|
1676 |
+
if (\is_float($computed)) {
|
1677 |
+
return $computed;
|
|
|
|
|
1678 |
}
|
1679 |
|
1680 |
+
// Resolve percentage values
|
1681 |
+
$font_size = $this->__get("font_size");
|
1682 |
+
return $this->single_length_in_pt($computed, $font_size);
|
1683 |
}
|
1684 |
|
1685 |
/**
|
1686 |
+
* @param float|string $computed
|
1687 |
* @return float
|
1688 |
+
*
|
1689 |
+
* @link https://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
|
1690 |
*/
|
1691 |
+
protected function _get_line_height($computed)
|
1692 |
{
|
1693 |
+
// Lengths have been computed to float, number values to string
|
1694 |
+
if (\is_float($computed)) {
|
1695 |
+
return $computed;
|
|
|
1696 |
}
|
1697 |
|
1698 |
+
$font_size = $this->__get("font_size");
|
1699 |
+
$factor = $computed === "normal"
|
1700 |
+
? self::$default_line_height
|
1701 |
+
: (float) $computed;
|
1702 |
|
1703 |
+
return $factor * $font_size;
|
1704 |
}
|
1705 |
|
1706 |
/**
|
1707 |
+
* @param string $computed
|
1708 |
+
* @param bool $current_is_parent
|
1709 |
+
*
|
1710 |
* @return array|string
|
1711 |
*/
|
1712 |
+
protected function get_color_value($computed, bool $current_is_parent = false)
|
1713 |
{
|
1714 |
+
if ($computed === "currentcolor") {
|
|
|
|
|
1715 |
// https://www.w3.org/TR/css-color-4/#resolving-other-colors
|
1716 |
if ($current_is_parent) {
|
1717 |
// Use the `color` value from the parent for the `color`
|
1718 |
// property itself
|
1719 |
return isset($this->parent_style)
|
1720 |
? $this->parent_style->__get("color")
|
1721 |
+
: $this->munge_color(self::$_defaults["color"]);
|
1722 |
}
|
1723 |
|
1724 |
return $this->__get("color");
|
1725 |
}
|
1726 |
|
1727 |
+
return $this->munge_color($computed) ?? "transparent";
|
1728 |
}
|
1729 |
|
1730 |
/**
|
1731 |
* Returns the color as an array
|
1732 |
*
|
1733 |
* The array has the following format:
|
1734 |
+
* `array(r, g, b, "r" => r, "g" => g, "b" => b, "alpha" => alpha, "hex" => "#rrggbb")`
|
1735 |
*
|
1736 |
+
* @param string $computed
|
1737 |
+
* @return array|string
|
1738 |
+
*
|
1739 |
+
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-color
|
1740 |
*/
|
1741 |
+
protected function _get_color($computed)
|
1742 |
{
|
1743 |
+
return $this->get_color_value($computed, true);
|
1744 |
}
|
1745 |
|
1746 |
/**
|
1747 |
* Returns the background color as an array
|
1748 |
*
|
1749 |
+
* See {@link Style::_get_color()} for format of the color array.
|
1750 |
*
|
1751 |
+
* @param string $computed
|
1752 |
+
* @return array|string
|
1753 |
+
*
|
1754 |
+
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-color
|
1755 |
*/
|
1756 |
+
protected function _get_background_color($computed)
|
1757 |
{
|
1758 |
+
return $this->get_color_value($computed);
|
1759 |
}
|
1760 |
|
1761 |
/**
|
1762 |
* Returns the background image URI, or "none"
|
1763 |
*
|
1764 |
+
* @param string $computed
|
1765 |
* @return string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1766 |
*
|
1767 |
+
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
|
|
|
|
|
|
|
|
|
1768 |
*/
|
1769 |
+
protected function _get_background_image($computed): string
|
1770 |
{
|
1771 |
+
return $this->_stylesheet->resolve_url($computed);
|
|
|
|
|
|
|
|
|
|
|
1772 |
}
|
1773 |
|
|
|
1774 |
/**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1775 |
* Returns the border color as an array
|
1776 |
*
|
1777 |
+
* See {@link Style::_get_color()} for format of the color array.
|
1778 |
*
|
1779 |
+
* @param string $computed
|
1780 |
+
* @return array|string
|
1781 |
+
*
|
1782 |
+
* @link https://www.w3.org/TR/CSS21/box.html#border-color-properties
|
1783 |
*/
|
1784 |
+
protected function _get_border_top_color($computed)
|
1785 |
{
|
1786 |
+
return $this->get_color_value($computed);
|
1787 |
}
|
1788 |
|
1789 |
/**
|
1790 |
+
* @param string $computed
|
1791 |
+
* @return array|string
|
1792 |
*/
|
1793 |
+
protected function _get_border_right_color($computed)
|
1794 |
{
|
1795 |
+
return $this->get_color_value($computed);
|
1796 |
}
|
1797 |
|
1798 |
/**
|
1799 |
+
* @param string $computed
|
1800 |
+
* @return array|string
|
1801 |
*/
|
1802 |
+
protected function _get_border_bottom_color($computed)
|
1803 |
{
|
1804 |
+
return $this->get_color_value($computed);
|
1805 |
}
|
1806 |
|
1807 |
/**
|
1808 |
+
* @param string $computed
|
1809 |
+
* @return array|string
|
1810 |
*/
|
1811 |
+
protected function _get_border_left_color($computed)
|
1812 |
{
|
1813 |
+
return $this->get_color_value($computed);
|
1814 |
}
|
1815 |
|
|
|
|
|
1816 |
/**
|
1817 |
* Return an array of all border properties.
|
1818 |
*
|
1819 |
* The returned array has the following structure:
|
1820 |
+
*
|
1821 |
+
* ```
|
1822 |
* array("top" => array("width" => [border-width],
|
1823 |
* "style" => [border-style],
|
1824 |
* "color" => [border-color (array)]),
|
1825 |
* "bottom" ... )
|
1826 |
+
* ```
|
1827 |
*
|
1828 |
* @return array
|
1829 |
*/
|
1830 |
+
public function get_border_properties(): array
|
1831 |
{
|
1832 |
return [
|
1833 |
"top" => [
|
1854 |
}
|
1855 |
|
1856 |
/**
|
1857 |
+
* Return a single border-side property
|
1858 |
*
|
1859 |
* @param string $side
|
1860 |
+
* @return string
|
|
|
1861 |
*/
|
1862 |
+
protected function get_border_side(string $side): string
|
1863 |
{
|
1864 |
+
$color = $this->__get("border_{$side}_color");
|
1865 |
|
1866 |
+
return $this->__get("border_{$side}_width") . " " .
|
1867 |
+
$this->__get("border_{$side}_style") . " " .
|
1868 |
+
(\is_array($color) ? $color["hex"] : $color);
|
1869 |
}
|
1870 |
|
1871 |
+
/**
|
1872 |
* Return full border properties as a string
|
1873 |
*
|
1874 |
* Border properties are returned just as specified in CSS:
|
1875 |
+
* `[width] [style] [color]`
|
1876 |
* e.g. "1px solid blue"
|
1877 |
*
|
|
|
1878 |
* @return string
|
1879 |
+
*
|
1880 |
+
* @link https://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
|
1881 |
*/
|
1882 |
+
protected function _get_border_top(): string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1883 |
{
|
1884 |
+
return $this->get_border_side("top");
|
1885 |
}
|
1886 |
|
1887 |
/**
|
1888 |
+
* @return string
|
1889 |
*/
|
1890 |
+
protected function _get_border_right(): string
|
1891 |
{
|
1892 |
+
return $this->get_border_side("right");
|
1893 |
}
|
1894 |
|
1895 |
/**
|
1896 |
+
* @return string
|
1897 |
*/
|
1898 |
+
protected function _get_border_bottom(): string
|
1899 |
{
|
1900 |
+
return $this->get_border_side("bottom");
|
1901 |
}
|
1902 |
|
1903 |
/**
|
1904 |
+
* @return string
|
|
|
|
|
|
|
1905 |
*/
|
1906 |
+
protected function _get_border_left(): string
|
1907 |
{
|
1908 |
+
return $this->get_border_side("left");
|
1909 |
}
|
1910 |
|
1911 |
public function has_border_radius(): bool
|
1917 |
// Use a fixed ref size here. We don't know the border-box width here
|
1918 |
// and font size might be 0. Since we are only interested in whether
|
1919 |
// there is any border radius at all, this should do
|
1920 |
+
$tl = (float) $this->length_in_pt($this->border_top_left_radius, 12);
|
1921 |
+
$tr = (float) $this->length_in_pt($this->border_top_right_radius, 12);
|
1922 |
+
$br = (float) $this->length_in_pt($this->border_bottom_right_radius, 12);
|
1923 |
+
$bl = (float) $this->length_in_pt($this->border_bottom_left_radius, 12);
|
1924 |
|
1925 |
$this->has_border_radius_cache = $tl + $tr + $br + $bl > 0;
|
1926 |
return $this->has_border_radius_cache;
|
2013 |
/**
|
2014 |
* Returns the outline color as an array
|
2015 |
*
|
2016 |
+
* See {@link Style::_get_color()} for format of the color array.
|
2017 |
*
|
2018 |
+
* @param string $computed
|
2019 |
+
* @return array|string
|
2020 |
+
*
|
2021 |
+
* @link https://www.w3.org/TR/css-ui-4/#propdef-outline-color
|
2022 |
+
*/
|
2023 |
+
protected function _get_outline_color($computed)
|
2024 |
+
{
|
2025 |
+
return $this->get_color_value($computed);
|
2026 |
+
}
|
2027 |
+
|
2028 |
+
/**
|
2029 |
+
* @param string $computed
|
2030 |
+
* @return string
|
2031 |
+
*
|
2032 |
+
* @link https://www.w3.org/TR/css-ui-4/#propdef-outline-style
|
2033 |
*/
|
2034 |
+
protected function _get_outline_style($computed): string
|
2035 |
{
|
2036 |
+
return $computed === "auto" ? "solid" : $computed;
|
2037 |
}
|
2038 |
|
2039 |
/**
|
2040 |
* Return full outline properties as a string
|
2041 |
*
|
2042 |
* Outline properties are returned just as specified in CSS:
|
2043 |
+
* `[width] [style] [color]`
|
2044 |
* e.g. "1px solid blue"
|
2045 |
*
|
|
|
2046 |
* @return string
|
2047 |
+
*
|
2048 |
+
* @link https://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
|
2049 |
*/
|
2050 |
+
protected function _get_outline(): string
|
2051 |
{
|
2052 |
$color = $this->__get("outline_color");
|
2053 |
|
2054 |
return $this->__get("outline_width") . " " .
|
2055 |
$this->__get("outline_style") . " " .
|
2056 |
+
(\is_array($color) ? $color["hex"] : $color);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2057 |
}
|
2058 |
|
2059 |
/**
|
2060 |
* Returns the list style image URI, or "none"
|
2061 |
*
|
2062 |
+
* @param string $computed
|
2063 |
* @return string
|
2064 |
+
*
|
2065 |
+
* @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
|
2066 |
*/
|
2067 |
+
protected function _get_list_style_image($computed): string
|
2068 |
{
|
2069 |
+
return $this->_stylesheet->resolve_url($computed);
|
2070 |
}
|
2071 |
|
2072 |
/**
|
2097 |
}
|
2098 |
|
2099 |
/**
|
2100 |
+
* @param string $computed
|
2101 |
* @return array|string
|
2102 |
+
*
|
2103 |
+
* @link https://www.w3.org/TR/CSS21/generate.html#propdef-counter-increment
|
2104 |
*/
|
2105 |
+
protected function _get_counter_increment($computed)
|
2106 |
{
|
2107 |
+
if ($computed === "none") {
|
2108 |
+
return $computed;
|
|
|
|
|
2109 |
}
|
2110 |
|
2111 |
+
return $this->parse_counter_prop($computed, 1);
|
2112 |
}
|
2113 |
|
2114 |
/**
|
2115 |
+
* @param string $computed
|
2116 |
* @return array|string
|
2117 |
+
*
|
2118 |
+
* @link https://www.w3.org/TR/CSS21/generate.html#propdef-counter-reset
|
2119 |
*/
|
2120 |
+
protected function _get_counter_reset($computed)
|
2121 |
{
|
2122 |
+
if ($computed === "none") {
|
2123 |
+
return $computed;
|
|
|
|
|
2124 |
}
|
2125 |
|
2126 |
+
return $this->parse_counter_prop($computed, 0);
|
2127 |
}
|
2128 |
|
2129 |
/**
|
2130 |
+
* @param string $computed
|
2131 |
* @return string[]|string
|
2132 |
+
*
|
2133 |
+
* @link https://www.w3.org/TR/CSS21/generate.html#propdef-content
|
2134 |
*/
|
2135 |
+
protected function _get_content($computed)
|
2136 |
{
|
2137 |
+
if ($computed === "normal" || $computed === "none") {
|
2138 |
+
return $computed;
|
|
|
|
|
2139 |
}
|
2140 |
|
2141 |
+
return $this->parse_property_value($computed);
|
2142 |
}
|
2143 |
|
2144 |
/*==============================*/
|
2180 |
|| preg_match("/^#|rgb\(|rgba\(|cmyk\(/", $val);
|
2181 |
}
|
2182 |
|
2183 |
+
/**
|
2184 |
+
* @param string $val
|
2185 |
+
* @return string|null
|
2186 |
+
*/
|
2187 |
+
protected function compute_color_value(string $val): ?string
|
2188 |
+
{
|
2189 |
+
// https://www.w3.org/TR/css-color-4/#resolving-other-colors
|
2190 |
+
$munged_color = $val !== "currentcolor"
|
2191 |
+
? $this->munge_color($val)
|
2192 |
+
: $val;
|
2193 |
+
|
2194 |
+
if ($munged_color === null) {
|
2195 |
+
return null;
|
2196 |
+
}
|
2197 |
+
|
2198 |
+
return \is_array($munged_color) ? $munged_color["hex"] : $munged_color;
|
2199 |
+
}
|
2200 |
+
|
2201 |
+
/**
|
2202 |
+
* @param string $val
|
2203 |
+
* @return int|null
|
2204 |
+
*/
|
2205 |
+
protected function compute_integer(string $val): ?int
|
2206 |
{
|
2207 |
+
$integer = self::CSS_INTEGER;
|
2208 |
+
return preg_match("/^$integer$/", $val)
|
2209 |
+
? (int) $val
|
2210 |
+
: null;
|
|
|
|
|
|
|
|
|
2211 |
}
|
2212 |
|
2213 |
/**
|
2214 |
+
* @param string $val
|
2215 |
+
* @return float|null
|
|
|
|
|
|
|
|
|
|
|
|
|
2216 |
*/
|
2217 |
+
protected function compute_length(string $val): ?float
|
2218 |
{
|
2219 |
+
return mb_strpos($val, "%") === false
|
2220 |
+
? $this->single_length_in_pt($val)
|
2221 |
+
: null;
|
2222 |
+
}
|
2223 |
|
2224 |
+
/**
|
2225 |
+
* @param string $val
|
2226 |
+
* @return float|null
|
2227 |
+
*/
|
2228 |
+
protected function compute_length_positive(string $val): ?float
|
2229 |
+
{
|
2230 |
+
$computed = $this->compute_length($val);
|
2231 |
+
return $computed !== null && $computed >= 0 ? $computed : null;
|
2232 |
+
}
|
2233 |
|
2234 |
+
/**
|
2235 |
+
* @param string $val
|
2236 |
+
* @return float|string|null
|
2237 |
+
*/
|
2238 |
+
protected function compute_length_percentage(string $val)
|
2239 |
+
{
|
2240 |
+
// Compute with a fixed ref size to decide whether percentage values
|
2241 |
+
// are valid
|
2242 |
+
$computed = $this->single_length_in_pt($val, 12);
|
2243 |
|
2244 |
+
if ($computed === null) {
|
2245 |
+
return null;
|
2246 |
}
|
2247 |
|
2248 |
+
// Retain valid percentage declarations
|
2249 |
+
return mb_strpos($val, "%") === false ? $computed : $val;
|
2250 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2251 |
|
2252 |
+
/**
|
2253 |
+
* @param string $val
|
2254 |
+
* @return float|string|null
|
2255 |
+
*/
|
2256 |
+
protected function compute_length_percentage_positive(string $val)
|
2257 |
+
{
|
2258 |
+
// Compute with a fixed ref size to decide whether percentage values
|
2259 |
+
// are valid
|
2260 |
+
$computed = $this->single_length_in_pt($val, 12);
|
2261 |
|
2262 |
+
if ($computed === null || $computed < 0) {
|
2263 |
+
return null;
|
2264 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2265 |
|
2266 |
+
// Retain valid percentage declarations
|
2267 |
+
return mb_strpos($val, "%") === false ? $computed : $val;
|
2268 |
+
}
|
|
|
2269 |
|
2270 |
+
/**
|
2271 |
+
* @param string $val
|
2272 |
+
* @param string $style_prop The corresponding border-/outline-style property.
|
2273 |
+
*
|
2274 |
+
* @return float|null
|
2275 |
+
*
|
2276 |
+
* @link https://www.w3.org/TR/css-backgrounds-3/#typedef-line-width
|
2277 |
+
*/
|
2278 |
+
protected function compute_line_width(string $val, string $style_prop): ?float
|
2279 |
+
{
|
2280 |
+
// Border-width keywords
|
2281 |
+
if ($val === "thin") {
|
2282 |
+
$computed = 0.5;
|
2283 |
+
} elseif ($val === "medium") {
|
2284 |
+
$computed = 1.5;
|
2285 |
+
} elseif ($val === "thick") {
|
2286 |
+
$computed = 2.5;
|
2287 |
} else {
|
2288 |
+
$computed = $this->compute_length_positive($val);
|
2289 |
+
}
|
2290 |
+
|
2291 |
+
if ($computed === null) {
|
2292 |
+
return null;
|
2293 |
}
|
2294 |
+
|
2295 |
+
// Computed width is 0 if the line style is `none` or `hidden`
|
2296 |
+
// https://www.w3.org/TR/css-backgrounds-3/#border-width
|
2297 |
+
// https://www.w3.org/TR/css-ui-4/#outline-width
|
2298 |
+
$lineStyle = $this->__get($style_prop);
|
2299 |
+
$hasLineStyle = $lineStyle !== "none" && $lineStyle !== "hidden";
|
2300 |
+
|
2301 |
+
return $hasLineStyle ? $computed : 0.0;
|
2302 |
+
}
|
2303 |
+
|
2304 |
+
/**
|
2305 |
+
* @param string $val
|
2306 |
+
* @return string|null
|
2307 |
+
*/
|
2308 |
+
protected function compute_border_style(string $val): ?string
|
2309 |
+
{
|
2310 |
+
return \in_array($val, self::BORDER_STYLES, true) ? $val : null;
|
2311 |
}
|
2312 |
|
2313 |
/**
|
2314 |
+
* Parse a property value with 1 to 4 components into 4 values, as required
|
2315 |
+
* by shorthand properties such as `margin`, `padding`, and `border-radius`.
|
2316 |
+
*
|
2317 |
+
* @param string $prop The shorthand property with exactly 4 sub-properties to handle.
|
2318 |
+
* @param string $value The property value to parse.
|
2319 |
+
*
|
2320 |
+
* @return string[]
|
2321 |
*/
|
2322 |
+
protected function set_quad_shorthand(string $prop, string $value): array
|
2323 |
{
|
2324 |
+
$v = $this->parse_property_value($value);
|
2325 |
|
2326 |
+
switch (\count($v)) {
|
2327 |
case 1:
|
2328 |
+
$values = [$v[0], $v[0], $v[0], $v[0]];
|
2329 |
break;
|
2330 |
case 2:
|
2331 |
+
$values = [$v[0], $v[1], $v[0], $v[1]];
|
2332 |
break;
|
2333 |
case 3:
|
2334 |
+
$values = [$v[0], $v[1], $v[2], $v[1]];
|
2335 |
break;
|
2336 |
case 4:
|
2337 |
+
$values = [$v[0], $v[1], $v[2], $v[3]];
|
2338 |
break;
|
2339 |
default:
|
2340 |
+
return [];
|
2341 |
}
|
2342 |
|
2343 |
+
return array_combine(self::$_props_shorthand[$prop], $values);
|
|
|
|
|
|
|
2344 |
}
|
2345 |
|
2346 |
/*======================*/
|
2347 |
|
2348 |
/**
|
2349 |
+
* @link https://www.w3.org/TR/CSS21/visuren.html#display-prop
|
|
|
|
|
2350 |
*/
|
2351 |
+
protected function _compute_display(string $val)
|
2352 |
{
|
2353 |
// Make sure that common valid, but unsupported display types have an
|
2354 |
// appropriate fallback display type
|
2366 |
}
|
2367 |
|
2368 |
if (!isset(self::$valid_display_types[$val])) {
|
2369 |
+
return null;
|
2370 |
}
|
2371 |
|
2372 |
// https://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
|
2373 |
if ($this->is_in_flow()) {
|
2374 |
+
return $val;
|
2375 |
} else {
|
2376 |
switch ($val) {
|
2377 |
case "inline":
|
2384 |
// case "table-column-group":
|
2385 |
// case "table-column":
|
2386 |
// case "table-caption":
|
2387 |
+
return "block";
|
|
|
2388 |
case "inline-table":
|
2389 |
+
return "table";
|
|
|
2390 |
default:
|
2391 |
+
return $val;
|
|
|
2392 |
}
|
2393 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2394 |
}
|
2395 |
|
2396 |
/**
|
2397 |
+
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-color
|
|
|
|
|
|
|
|
|
|
|
2398 |
*/
|
2399 |
+
protected function _compute_color(string $color)
|
2400 |
{
|
2401 |
+
return $this->compute_color_value($color);
|
2402 |
}
|
2403 |
|
2404 |
/**
|
2405 |
+
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-color
|
|
|
|
|
|
|
2406 |
*/
|
2407 |
+
protected function _compute_background_color(string $color)
|
2408 |
{
|
2409 |
+
return $this->compute_color_value($color);
|
2410 |
}
|
2411 |
|
2412 |
/**
|
|
|
2413 |
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
|
|
|
|
|
2414 |
*/
|
2415 |
+
protected function _compute_background_image(string $val)
|
2416 |
{
|
|
|
|
|
2417 |
$parsed_val = $this->_stylesheet->resolve_url($val);
|
2418 |
|
2419 |
if ($parsed_val === "none") {
|
2420 |
+
return "none";
|
2421 |
} else {
|
2422 |
+
return "url($parsed_val)";
|
2423 |
}
|
2424 |
}
|
2425 |
|
2426 |
/**
|
2427 |
+
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
|
|
|
|
|
|
|
2428 |
*/
|
2429 |
+
protected function _compute_background_repeat(string $val)
|
2430 |
{
|
2431 |
+
$keywords = ["repeat", "repeat-x", "repeat-y", "no-repeat"];
|
2432 |
+
return \in_array($val, $keywords, true) ? $val : null;
|
|
|
|
|
|
|
|
|
|
|
|
|
2433 |
}
|
2434 |
|
2435 |
/**
|
2436 |
+
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
|
|
|
|
|
|
|
2437 |
*/
|
2438 |
+
protected function _compute_background_attachment(string $val)
|
2439 |
{
|
2440 |
+
$keywords = ["scroll", "fixed"];
|
2441 |
+
return \in_array($val, $keywords, true) ? $val : null;
|
|
|
|
|
|
|
|
|
|
|
|
|
2442 |
}
|
2443 |
|
2444 |
/**
|
2445 |
+
* @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-position
|
|
|
|
|
|
|
2446 |
*/
|
2447 |
+
protected function _compute_background_position(string $val)
|
2448 |
{
|
2449 |
+
$parts = preg_split("/\s+/", $val);
|
2450 |
|
2451 |
+
if (\count($parts) > 2) {
|
2452 |
+
return null;
|
2453 |
+
}
|
2454 |
|
2455 |
+
switch ($parts[0]) {
|
2456 |
case "left":
|
2457 |
$x = "0%";
|
2458 |
break;
|
2475 |
break;
|
2476 |
|
2477 |
default:
|
2478 |
+
$x = $parts[0];
|
2479 |
break;
|
2480 |
}
|
2481 |
|
2482 |
+
if (isset($parts[1])) {
|
2483 |
+
switch ($parts[1]) {
|
2484 |
case "left":
|
2485 |
$x = "0%";
|
2486 |
break;
|
2498 |
break;
|
2499 |
|
2500 |
case "center":
|
2501 |
+
if ($parts[0] === "left" || $parts[0] === "right" || $parts[0] === "center") {
|
2502 |
$y = "50%";
|
2503 |
} else {
|
2504 |
$x = "50%";
|
2506 |
break;
|
2507 |
|
2508 |
default:
|
2509 |
+
$y = $parts[1];
|
2510 |
break;
|
2511 |
}
|
2512 |
} else {
|
2520 |
if (!isset($y)) {
|
2521 |
$y = "0%";
|
2522 |
}
|
2523 |
+
|
2524 |
+
return [$x, $y];
|
2525 |
}
|
2526 |
|
2527 |
/**
|
2528 |
+
* Compute `background-size`.
|
2529 |
*
|
2530 |
+
* Computes to one of the following values:
|
2531 |
+
* * `cover`
|
2532 |
+
* * `contain`
|
2533 |
+
* * `[width, height]`, each being a length, percentage, or `auto`
|
2534 |
+
*
|
2535 |
+
* @link https://www.w3.org/TR/css-backgrounds-3/#background-size
|
2536 |
*/
|
2537 |
+
protected function _compute_background_size(string $val)
|
2538 |
{
|
2539 |
+
if ($val === "cover" || $val === "contain") {
|
2540 |
+
return $val;
|
2541 |
+
}
|
2542 |
|
2543 |
+
$parts = preg_split("/\s+/", $val);
|
|
|
2544 |
|
2545 |
+
if (\count($parts) > 2) {
|
2546 |
+
return null;
|
2547 |
+
}
|
2548 |
+
|
2549 |
+
$width = $parts[0];
|
2550 |
+
if ($width !== "auto") {
|
2551 |
+
$width = $this->compute_length_percentage_positive($width);
|
|
|
2552 |
}
|
2553 |
|
2554 |
+
$height = $parts[1] ?? "auto";
|
2555 |
+
if ($height !== "auto") {
|
2556 |
+
$height = $this->compute_length_percentage_positive($height);
|
2557 |
}
|
2558 |
|
2559 |
+
if ($width === null || $height === null) {
|
2560 |
+
return null;
|
|
|
2561 |
}
|
2562 |
|
2563 |
+
return [$width, $height];
|
2564 |
}
|
2565 |
|
2566 |
/**
|
2567 |
+
* @link https://www.w3.org/TR/css-backgrounds-3/#propdef-background
|
|
|
|
|
|
|
|
|
2568 |
*/
|
2569 |
+
protected function _set_background(string $value): array
|
2570 |
{
|
2571 |
+
$components = $this->parse_property_value($value);
|
2572 |
+
$props = [];
|
2573 |
+
$pos_size = [];
|
2574 |
+
|
2575 |
+
foreach ($components as $val) {
|
2576 |
+
if ($val === "none" || mb_substr($val, 0, 4) === "url(") {
|
2577 |
+
$props["background_image"] = $val;
|
2578 |
+
} elseif ($val === "scroll" || $val === "fixed") {
|
2579 |
+
$props["background_attachment"] = $val;
|
2580 |
+
} elseif ($val === "repeat" || $val === "repeat-x" || $val === "repeat-y" || $val === "no-repeat") {
|
2581 |
+
$props["background_repeat"] = $val;
|
2582 |
+
} elseif ($this->is_color_value($val)) {
|
2583 |
+
$props["background_color"] = $val;
|
2584 |
+
} else {
|
2585 |
+
$pos_size[] = $val;
|
|
|
|
|
|
|
|
|
2586 |
}
|
2587 |
+
}
|
2588 |
|
2589 |
+
if (\count($pos_size)) {
|
2590 |
+
// Split value list at "/"
|
2591 |
+
$index = array_search("/", $pos_size, true);
|
2592 |
|
2593 |
+
if ($index !== false) {
|
2594 |
+
$pos = \array_slice($pos_size, 0, $index);
|
2595 |
+
$size = \array_slice($pos_size, $index + 1);
|
2596 |
+
} else {
|
2597 |
+
$pos = $pos_size;
|
2598 |
+
$size = [];
|
2599 |
+
}
|
2600 |
|
2601 |
+
$props["background_position"] = implode(" ", $pos);
|
2602 |
|
2603 |
+
if (\count($size)) {
|
2604 |
+
$props["background_size"] = implode(" ", $size);
|
|
|
2605 |
}
|
2606 |
}
|
2607 |
+
|
2608 |
+
return $props;
|
2609 |
}
|
2610 |
|
2611 |
/**
|
2612 |
+
* @link https://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
|
|
|
|
|
|
|
|
|
|
|
2613 |
*/
|
2614 |
+
protected function _compute_font_size(string $size)
|
2615 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2616 |
$parent_font_size = isset($this->parent_style)
|
2617 |
? $this->parent_style->__get("font_size")
|
2618 |
: self::$default_font_size;
|
2619 |
|
2620 |
+
switch ($size) {
|
2621 |
case "xx-small":
|
2622 |
case "x-small":
|
2623 |
case "small":
|
2641 |
break;
|
2642 |
}
|
2643 |
|
2644 |
+
return $fs;
|
2645 |
}
|
2646 |
|
2647 |
/**
|
2648 |
+
* @link https://www.w3.org/TR/CSS21/fonts.html#font-boldness
|
|
|
|
|
2649 |
*/
|
2650 |
+
protected function _compute_font_weight(string $weight)
|
2651 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2652 |
$computed_weight = $weight;
|
2653 |
|
2654 |
if ($weight === "bolder") {
|
2659 |
$computed_weight = "normal";
|
2660 |
}
|
2661 |
|
2662 |
+
return $computed_weight;
|
2663 |
}
|
2664 |
|
2665 |
/**
|
2666 |
+
* Handle the `font` shorthand property.
|
2667 |
*
|
2668 |
+
* `[ font-style || font-variant || font-weight ] font-size [ / line-height ] font-family`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2669 |
*
|
2670 |
* @link https://www.w3.org/TR/CSS21/fonts.html#font-shorthand
|
|
|
|
|
2671 |
*/
|
2672 |
+
protected function _set_font(string $value): array
|
2673 |
{
|
2674 |
+
$components = $this->parse_property_value($value);
|
2675 |
+
$props = [];
|
2676 |
+
|
2677 |
+
$number = self::CSS_NUMBER;
|
2678 |
+
$unit = "pt|px|pc|rem|em|ex|in|cm|mm|%";
|
2679 |
+
$sizePattern = "/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|$number(?:$unit))$/";
|
2680 |
+
$sizeIndex = null;
|
2681 |
+
|
2682 |
+
// Find index of font-size to split the component list
|
2683 |
+
foreach ($components as $i => $val) {
|
2684 |
+
if (preg_match($sizePattern, $val)) {
|
2685 |
+
$sizeIndex = $i;
|
2686 |
+
$props["font_size"] = $val;
|
2687 |
+
break;
|
2688 |
+
}
|
2689 |
}
|
2690 |
|
2691 |
+
// `font-size` is mandatory
|
2692 |
+
if ($sizeIndex === null) {
|
2693 |
+
return [];
|
2694 |
}
|
2695 |
|
2696 |
+
// `font-style`, `font-variant`, `font-weight` in any order
|
2697 |
+
$styleVariantWeight = \array_slice($components, 0, $sizeIndex);
|
2698 |
+
$stylePattern = "/^(italic|oblique)$/";
|
2699 |
+
$variantPattern = "/^(small-caps)$/";
|
2700 |
+
$weightPattern = "/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900)$/";
|
2701 |
+
|
2702 |
+
if (\count($styleVariantWeight) > 3) {
|
2703 |
+
return [];
|
2704 |
}
|
2705 |
|
2706 |
+
foreach ($styleVariantWeight as $val) {
|
2707 |
+
if ($val === "normal") {
|
2708 |
+
// Ignore any `normal` value, as it is valid and the initial
|
2709 |
+
// value for all three properties
|
2710 |
+
} elseif (!isset($props["font_style"]) && preg_match($stylePattern, $val)) {
|
2711 |
+
$props["font_style"] = $val;
|
2712 |
+
} elseif (!isset($props["font_variant"]) && preg_match($variantPattern, $val)) {
|
2713 |
+
$props["font_variant"] = $val;
|
2714 |
+
} elseif (!isset($props["font_weight"]) && preg_match($weightPattern, $val)) {
|
2715 |
+
$props["font_weight"] = $val;
|
2716 |
+
} else {
|
2717 |
+
// Duplicates and other values disallowed here
|
2718 |
+
return [];
|
2719 |
}
|
2720 |
}
|
2721 |
|
2722 |
+
// Optional slash + `line-height` followed by mandatory `font-family`
|
2723 |
+
$lineFamily = \array_slice($components, $sizeIndex + 1);
|
2724 |
+
$hasLineHeight = $lineFamily !== [] && $lineFamily[0] === "/";
|
2725 |
+
$lineHeight = $hasLineHeight ? \array_slice($lineFamily, 1, 1) : [];
|
2726 |
+
$fontFamily = $hasLineHeight ? \array_slice($lineFamily, 2) : $lineFamily;
|
2727 |
+
$lineHeightPattern = "/^(normal|$number(?:$unit)?)$/";
|
2728 |
+
|
2729 |
+
// Missing `font-family` or `line-height` after slash
|
2730 |
+
if ($fontFamily === []
|
2731 |
+
|| ($hasLineHeight && !preg_match($lineHeightPattern, $lineHeight[0]))
|
2732 |
+
) {
|
2733 |
+
return [];
|
2734 |
+
}
|
2735 |
+
|
2736 |
+
if ($hasLineHeight) {
|
2737 |
+
$props["line_height"] = $lineHeight[0];
|
2738 |
}
|
2739 |
+
|
2740 |
+
$props["font_family"] = implode("", $fontFamily);
|
2741 |
+
|
2742 |
+
return $props;
|
2743 |
}
|
2744 |
|
2745 |
/**
|
2746 |
+
* Compute `text-align`.
|
2747 |
*
|
2748 |
* If no alignment is set on the element and the direction is rtl then
|
2749 |
* the property is set to "right", otherwise it is set to "left".
|
2750 |
*
|
2751 |
* @link https://www.w3.org/TR/CSS21/text.html#propdef-text-align
|
2752 |
*/
|
2753 |
+
protected function _compute_text_align(string $val)
|
2754 |
{
|
|
|
|
|
2755 |
$alignment = $val;
|
2756 |
if ($alignment === "") {
|
2757 |
$alignment = "left";
|
2760 |
}
|
2761 |
}
|
2762 |
|
2763 |
+
if (!\in_array($alignment, self::TEXT_ALIGN_KEYWORDS, true)) {
|
2764 |
+
return null;
|
|
|
2765 |
}
|
2766 |
|
2767 |
+
return $alignment;
|
2768 |
}
|
2769 |
|
2770 |
/**
|
2771 |
+
* @link https://www.w3.org/TR/css-text-4/#word-spacing-property
|
|
|
|
|
|
|
2772 |
*/
|
2773 |
+
protected function _compute_word_spacing(string $val)
|
2774 |
{
|
2775 |
+
if ($val === "normal") {
|
2776 |
+
return 0.0;
|
|
|
|
|
|
|
2777 |
}
|
2778 |
|
2779 |
+
return $this->compute_length_percentage($val);
|
|
|
|
|
|
|
|
|
2780 |
}
|
2781 |
|
2782 |
/**
|
2783 |
+
* @link https://www.w3.org/TR/css-text-4/#letter-spacing-property
|
|
|
|
|
|
|
2784 |
*/
|
2785 |
+
protected function _compute_letter_spacing(string $val)
|
2786 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2787 |
if ($val === "normal") {
|
2788 |
+
return 0.0;
|
|
|
|
|
2789 |
}
|
2790 |
+
|
2791 |
+
return $this->compute_length_percentage($val);
|
2792 |
}
|
2793 |
|
2794 |
/**
|
2795 |
+
* @link https://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
|
|
|
|
|
|
|
2796 |
*/
|
2797 |
+
protected function _compute_line_height(string $val)
|
2798 |
{
|
2799 |
+
if ($val === "normal") {
|
2800 |
+
return $val;
|
|
|
|
|
|
|
2801 |
}
|
2802 |
|
2803 |
+
// Compute number values to string and lengths to float (in pt)
|
2804 |
+
if (is_numeric($val)) {
|
2805 |
+
return (string) $val;
|
|
|
2806 |
}
|
2807 |
+
|
2808 |
+
$font_size = $this->__get("font_size");
|
2809 |
+
$computed = $this->single_length_in_pt($val, $font_size);
|
2810 |
+
return $computed !== null && $computed >= 0 ? $computed : null;
|
2811 |
}
|
2812 |
|
2813 |
/**
|
2814 |
+
* @link https://www.w3.org/TR/css-text-3/#text-indent-property
|
|
|
|
|
|
|
2815 |
*/
|
2816 |
+
protected function _compute_text_indent(string $val)
|
2817 |
{
|
2818 |
+
return $this->compute_length_percentage($val);
|
2819 |
+
}
|
|
|
|
|
|
|
|
|
2820 |
|
2821 |
+
/**
|
2822 |
+
* @link https://www.w3.org/TR/CSS21/page.html#propdef-page-break-before
|
2823 |
+
*/
|
2824 |
+
protected function _compute_page_break_before(string $break)
|
2825 |
+
{
|
2826 |
if ($break === "left" || $break === "right") {
|
2827 |
$break = "always";
|
2828 |
}
|
2829 |
|
2830 |
+
return $break;
|
2831 |
}
|
2832 |
|
2833 |
/**
|
2834 |
+
* @link https://www.w3.org/TR/CSS21/page.html#propdef-page-break-after
|
2835 |
*/
|
2836 |
+
protected function _compute_page_break_after(string $break)
|
2837 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2838 |
if ($break === "left" || $break === "right") {
|
2839 |
$break = "always";
|
2840 |
}
|
2841 |
|
2842 |
+
return $break;
|
2843 |
}
|
2844 |
|
2845 |
/**
|
2846 |
+
* @link https://www.w3.org/TR/CSS21/visudet.html#propdef-width
|
|
|
|
|
|
|
2847 |
*/
|
2848 |
+
protected function _compute_width(string $val)
|
2849 |
{
|
2850 |
+
if ($val === "auto") {
|
2851 |
+
return $val;
|
2852 |
+
}
|
2853 |
+
|
2854 |
+
return $this->compute_length_percentage_positive($val);
|
2855 |
}
|
2856 |
|
2857 |
/**
|
2858 |
+
* @link https://www.w3.org/TR/CSS21/visudet.html#propdef-height
|
2859 |
*/
|
2860 |
+
protected function _compute_height(string $val)
|
2861 |
{
|
2862 |
+
if ($val === "auto") {
|
2863 |
+
return $val;
|
2864 |
+
}
|
2865 |
+
|
2866 |
+
return $this->compute_length_percentage_positive($val);
|
2867 |
}
|
2868 |
|
2869 |
/**
|
2870 |
+
* @link https://www.w3.org/TR/CSS21/visudet.html#propdef-min-width
|
2871 |
*/
|
2872 |
+
protected function _compute_min_width(string $val)
|
2873 |
{
|
2874 |
+
// Legacy support for `none`, not covered by spec
|
2875 |
+
if ($val === "auto" || $val === "none") {
|
2876 |
+
return "auto";
|
2877 |
+
}
|
2878 |
+
|
2879 |
+
return $this->compute_length_percentage_positive($val);
|
2880 |
}
|
2881 |
|
2882 |
/**
|
2883 |
+
* @link https://www.w3.org/TR/CSS21/visudet.html#propdef-min-height
|
2884 |
*/
|
2885 |
+
protected function _compute_min_height(string $val)
|
2886 |
{
|
2887 |
+
// Legacy support for `none`, not covered by spec
|
2888 |
+
if ($val === "auto" || $val === "none") {
|
2889 |
+
return "auto";
|
2890 |
+
}
|
2891 |
+
|
2892 |
+
return $this->compute_length_percentage_positive($val);
|
2893 |
}
|
2894 |
|
2895 |
/**
|
2896 |
+
* @link https://www.w3.org/TR/CSS21/visudet.html#propdef-max-width
|
|
|
2897 |
*/
|
2898 |
+
protected function _compute_max_width(string $val)
|
2899 |
{
|
2900 |
+
// Legacy support for `auto`, not covered by spec
|
2901 |
+
if ($val === "none" || $val === "auto") {
|
2902 |
+
return "none";
|
2903 |
+
}
|
2904 |
+
|
2905 |
+
return $this->compute_length_percentage_positive($val);
|
2906 |
}
|
2907 |
|
2908 |
/**
|
2909 |
+
* @link https://www.w3.org/TR/CSS21/visudet.html#propdef-max-height
|
|
|
|
|
|
|
2910 |
*/
|
2911 |
+
protected function _compute_max_height(string $val)
|
2912 |
{
|
2913 |
+
// Legacy support for `auto`, not covered by spec
|
2914 |
+
if ($val === "none" || $val === "auto") {
|
2915 |
+
return "none";
|
2916 |
+
}
|
2917 |
+
|
2918 |
+
return $this->compute_length_percentage_positive($val);
|
2919 |
}
|
2920 |
|
2921 |
/**
|
2922 |
+
* @link https://www.w3.org/TR/css-position-3/#inset-properties
|
2923 |
+
* @link https://www.w3.org/TR/css-position-3/#propdef-inset
|
2924 |
*/
|
2925 |
+
protected function _set_inset(string $val): array
|
2926 |
{
|
2927 |
+
return $this->set_quad_shorthand("inset", $val);
|
2928 |
}
|
2929 |
|
2930 |
/**
|
2931 |
+
* @param string $val
|
2932 |
+
* @return float|string|null
|
2933 |
*/
|
2934 |
+
protected function compute_box_inset(string $val)
|
2935 |
{
|
2936 |
+
if ($val === "auto") {
|
2937 |
+
return $val;
|
2938 |
+
}
|
2939 |
+
|
2940 |
+
return $this->compute_length_percentage($val);
|
2941 |
}
|
2942 |
|
2943 |
+
protected function _compute_top(string $val)
|
|
|
|
|
|
|
2944 |
{
|
2945 |
+
return $this->compute_box_inset($val);
|
2946 |
+
}
|
2947 |
+
|
2948 |
+
protected function _compute_right(string $val)
|
2949 |
+
{
|
2950 |
+
return $this->compute_box_inset($val);
|
2951 |
+
}
|
2952 |
+
|
2953 |
+
protected function _compute_bottom(string $val)
|
2954 |
+
{
|
2955 |
+
return $this->compute_box_inset($val);
|
2956 |
+
}
|
2957 |
+
|
2958 |
+
protected function _compute_left(string $val)
|
2959 |
+
{
|
2960 |
+
return $this->compute_box_inset($val);
|
2961 |
}
|
2962 |
|
2963 |
/**
|
2964 |
+
* @link https://www.w3.org/TR/CSS21/box.html#margin-properties
|
2965 |
+
* @link https://www.w3.org/TR/CSS21/box.html#propdef-margin
|
2966 |
*/
|
2967 |
+
protected function _set_margin(string $val): array
|
2968 |
{
|
2969 |
+
return $this->set_quad_shorthand("margin", $val);
|
2970 |
}
|
2971 |
|
2972 |
/**
|
2973 |
+
* @param string $val
|
2974 |
+
* @return float|string|null
|
|
|
|
|
|
|
2975 |
*/
|
2976 |
+
protected function compute_margin(string $val)
|
2977 |
{
|
2978 |
+
// Legacy support for `none` keyword, not covered by spec
|
2979 |
+
if ($val === "none") {
|
2980 |
+
return 0.0;
|
2981 |
+
}
|
2982 |
|
2983 |
+
if ($val === "auto") {
|
2984 |
+
return $val;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2985 |
}
|
2986 |
+
|
2987 |
+
return $this->compute_length_percentage($val);
|
2988 |
}
|
2989 |
|
2990 |
+
protected function _compute_margin_top(string $val)
|
|
|
|
|
|
|
|
|
|
|
2991 |
{
|
2992 |
+
return $this->compute_margin($val);
|
2993 |
}
|
2994 |
|
2995 |
+
protected function _compute_margin_right(string $val)
|
2996 |
{
|
2997 |
+
return $this->compute_margin($val);
|
2998 |
}
|
2999 |
|
3000 |
+
protected function _compute_margin_bottom(string $val)
|
3001 |
{
|
3002 |
+
return $this->compute_margin($val);
|
3003 |
}
|
3004 |
|
3005 |
+
protected function _compute_margin_left(string $val)
|
3006 |
{
|
3007 |
+
return $this->compute_margin($val);
|
3008 |
}
|
3009 |
|
3010 |
/**
|
3011 |
+
* @link https://www.w3.org/TR/CSS21/box.html#padding-properties
|
3012 |
+
* @link https://www.w3.org/TR/CSS21/box.html#propdef-padding
|
3013 |
*/
|
3014 |
+
protected function _set_padding(string $val): array
|
3015 |
{
|
3016 |
+
return $this->set_quad_shorthand("padding", $val);
|
3017 |
}
|
3018 |
|
3019 |
+
/**
|
3020 |
+
* @param string $val
|
3021 |
+
* @return float|string|null
|
3022 |
+
*/
|
3023 |
+
protected function compute_padding(string $val)
|
3024 |
{
|
3025 |
+
// Legacy support for `none` keyword, not covered by spec
|
3026 |
+
if ($val === "none") {
|
3027 |
+
return 0.0;
|
3028 |
+
}
|
3029 |
+
|
3030 |
+
return $this->compute_length_percentage_positive($val);
|
3031 |
}
|
3032 |
|
3033 |
+
protected function _compute_padding_top(string $val)
|
3034 |
{
|
3035 |
+
return $this->compute_padding($val);
|
3036 |
}
|
3037 |
|
3038 |
+
protected function _compute_padding_right(string $val)
|
3039 |
{
|
3040 |
+
return $this->compute_padding($val);
|
3041 |
}
|
3042 |
|
3043 |
+
protected function _compute_padding_bottom(string $val)
|
|
|
|
|
|
|
|
|
3044 |
{
|
3045 |
+
return $this->compute_padding($val);
|
3046 |
}
|
3047 |
|
3048 |
+
protected function _compute_padding_left(string $val)
|
3049 |
{
|
3050 |
+
return $this->compute_padding($val);
|
3051 |
}
|
3052 |
|
3053 |
+
/**
|
3054 |
+
* @param string $value `width || style || color`
|
3055 |
+
* @param string[] $styles The list of border styles to accept.
|
3056 |
+
*
|
3057 |
+
* @return array Array of `[width, style, color]`, or `null` if the declaration is invalid.
|
3058 |
+
*/
|
3059 |
+
protected function parse_border_side(string $value, array $styles = self::BORDER_STYLES): ?array
|
3060 |
{
|
3061 |
+
$components = $this->parse_property_value($value);
|
3062 |
+
$width = null;
|
3063 |
+
$style = null;
|
3064 |
+
$color = null;
|
3065 |
+
|
3066 |
+
foreach ($components as $val) {
|
3067 |
+
if ($style === null && \in_array($val, $styles, true)) {
|
3068 |
+
$style = $val;
|
3069 |
+
} elseif ($color === null && $this->is_color_value($val)) {
|
3070 |
+
$color = $val;
|
3071 |
+
} elseif ($width === null) {
|
3072 |
+
// Assume width
|
3073 |
+
$width = $val;
|
3074 |
+
} else {
|
3075 |
+
// Duplicates are not allowed
|
3076 |
+
return null;
|
3077 |
+
}
|
3078 |
+
}
|
3079 |
+
|
3080 |
+
return [$width, $style, $color];
|
3081 |
}
|
3082 |
|
3083 |
+
/**
|
3084 |
+
* @link https://www.w3.org/TR/CSS21/box.html#border-properties
|
3085 |
+
* @link https://www.w3.org/TR/CSS21/box.html#propdef-border
|
3086 |
+
*/
|
3087 |
+
protected function _set_border(string $value): array
|
3088 |
{
|
3089 |
+
$values = $this->parse_border_side($value);
|
3090 |
+
|
3091 |
+
if ($values === null) {
|
3092 |
+
return [];
|
3093 |
+
}
|
3094 |
+
|
3095 |
+
return array_merge(
|
3096 |
+
array_combine(self::$_props_shorthand["border_top"], $values),
|
3097 |
+
array_combine(self::$_props_shorthand["border_right"], $values),
|
3098 |
+
array_combine(self::$_props_shorthand["border_bottom"], $values),
|
3099 |
+
array_combine(self::$_props_shorthand["border_left"], $values)
|
3100 |
+
);
|
3101 |
}
|
3102 |
|
3103 |
/**
|
3104 |
+
* @param string $prop
|
3105 |
+
* @param string $value
|
3106 |
+
* @return array
|
3107 |
*/
|
3108 |
+
protected function set_border_side(string $prop, string $value): array
|
3109 |
{
|
3110 |
+
$values = $this->parse_border_side($value);
|
3111 |
+
|
3112 |
+
if ($values === null) {
|
3113 |
+
return [];
|
3114 |
+
}
|
3115 |
+
|
3116 |
+
return array_combine(self::$_props_shorthand[$prop], $values);
|
3117 |
}
|
3118 |
|
3119 |
+
protected function _set_border_top(string $val): array
|
3120 |
{
|
3121 |
+
return $this->set_border_side("border_top", $val);
|
3122 |
}
|
3123 |
|
3124 |
+
protected function _set_border_right(string $val): array
|
3125 |
{
|
3126 |
+
return $this->set_border_side("border_right", $val);
|
3127 |
}
|
3128 |
|
3129 |
+
protected function _set_border_bottom(string $val): array
|
3130 |
{
|
3131 |
+
return $this->set_border_side("border_bottom", $val);
|
3132 |
}
|
3133 |
|
3134 |
+
protected function _set_border_left(string $val): array
|
|
|
|
|
|
|
|
|
3135 |
{
|
3136 |
+
return $this->set_border_side("border_left", $val);
|
|
|
|
|
|
|
3137 |
}
|
3138 |
|
3139 |
/**
|
3140 |
+
* @link https://www.w3.org/TR/CSS21/box.html#propdef-border-color
|
|
|
3141 |
*/
|
3142 |
+
protected function _set_border_color(string $val): array
|
3143 |
{
|
3144 |
+
return $this->set_quad_shorthand("border_color", $val);
|
3145 |
}
|
3146 |
|
3147 |
+
protected function _compute_border_top_color(string $val)
|
|
|
|
|
|
|
|
|
3148 |
{
|
3149 |
+
return $this->compute_color_value($val);
|
3150 |
}
|
3151 |
|
3152 |
+
protected function _compute_border_right_color(string $val)
|
|
|
|
|
|
|
|
|
3153 |
{
|
3154 |
+
return $this->compute_color_value($val);
|
3155 |
}
|
3156 |
|
3157 |
+
protected function _compute_border_bottom_color(string $val)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3158 |
{
|
3159 |
+
return $this->compute_color_value($val);
|
3160 |
}
|
3161 |
|
3162 |
+
protected function _compute_border_left_color(string $val)
|
|
|
|
|
|
|
3163 |
{
|
3164 |
+
return $this->compute_color_value($val);
|
3165 |
}
|
3166 |
|
3167 |
/**
|
3168 |
+
* @link https://www.w3.org/TR/CSS21/box.html#propdef-border-style
|
3169 |
*/
|
3170 |
+
protected function _set_border_style(string $val): array
|
3171 |
{
|
3172 |
+
return $this->set_quad_shorthand("border_style", $val);
|
3173 |
}
|
3174 |
|
3175 |
+
protected function _compute_border_top_style(string $val)
|
|
|
|
|
|
|
3176 |
{
|
3177 |
+
return $this->compute_border_style($val);
|
3178 |
}
|
3179 |
|
3180 |
+
protected function _compute_border_right_style(string $val)
|
|
|
|
|
|
|
|
|
3181 |
{
|
3182 |
+
return $this->compute_border_style($val);
|
3183 |
+
}
|
3184 |
|
3185 |
+
protected function _compute_border_bottom_style(string $val)
|
3186 |
+
{
|
3187 |
+
return $this->compute_border_style($val);
|
3188 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3189 |
|
3190 |
+
protected function _compute_border_left_style(string $val)
|
3191 |
+
{
|
3192 |
+
return $this->compute_border_style($val);
|
|
|
3193 |
}
|
3194 |
|
3195 |
/**
|
3196 |
+
* @link https://www.w3.org/TR/CSS21/box.html#propdef-border-width
|
|
|
3197 |
*/
|
3198 |
+
protected function _set_border_width(string $val): array
|
3199 |
{
|
3200 |
+
return $this->set_quad_shorthand("border_width", $val);
|
3201 |
+
}
|
3202 |
|
3203 |
+
protected function _compute_border_top_width(string $val)
|
3204 |
+
{
|
3205 |
+
return $this->compute_line_width($val, "border_top_style");
|
3206 |
+
}
|
3207 |
|
3208 |
+
protected function _compute_border_right_width(string $val)
|
3209 |
+
{
|
3210 |
+
return $this->compute_line_width($val, "border_right_style");
|
3211 |
+
}
|
3212 |
|
3213 |
+
protected function _compute_border_bottom_width(string $val)
|
3214 |
+
{
|
3215 |
+
return $this->compute_line_width($val, "border_bottom_style");
|
3216 |
+
}
|
3217 |
|
3218 |
+
protected function _compute_border_left_width(string $val)
|
3219 |
+
{
|
3220 |
+
return $this->compute_line_width($val, "border_left_style");
|
3221 |
}
|
3222 |
|
3223 |
/**
|
3224 |
+
* @link https://www.w3.org/TR/css-backgrounds-3/#corners
|
3225 |
+
* @link https://www.w3.org/TR/css-backgrounds-3/#propdef-border-radius
|
3226 |
*/
|
3227 |
+
protected function _set_border_radius(string $val): array
|
3228 |
{
|
3229 |
+
return $this->set_quad_shorthand("border_radius", $val);
|
3230 |
}
|
3231 |
|
3232 |
+
protected function _compute_border_top_left_radius(string $val)
|
|
|
|
|
|
|
3233 |
{
|
3234 |
+
return $this->compute_length_percentage_positive($val);
|
3235 |
}
|
3236 |
|
3237 |
+
protected function _compute_border_top_right_radius(string $val)
|
|
|
|
|
|
|
3238 |
{
|
3239 |
+
return $this->compute_length_percentage_positive($val);
|
3240 |
}
|
3241 |
|
3242 |
+
protected function _compute_border_bottom_right_radius(string $val)
|
|
|
|
|
|
|
3243 |
{
|
3244 |
+
return $this->compute_length_percentage_positive($val);
|
3245 |
+
}
|
3246 |
+
|
3247 |
+
protected function _compute_border_bottom_left_radius(string $val)
|
3248 |
+
{
|
3249 |
+
return $this->compute_length_percentage_positive($val);
|
3250 |
}
|
3251 |
|
3252 |
/**
|
3253 |
+
* @link https://www.w3.org/TR/css-ui-4/#outline-props
|
3254 |
+
* @link https://www.w3.org/TR/css-ui-4/#propdef-outline
|
3255 |
*/
|
3256 |
+
protected function _set_outline(string $value): array
|
3257 |
{
|
3258 |
+
$values = $this->parse_border_side($value, self::OUTLINE_STYLES);
|
3259 |
|
3260 |
+
if ($values === null) {
|
3261 |
+
return [];
|
3262 |
}
|
3263 |
|
3264 |
+
return array_combine(self::$_props_shorthand["outline"], $values);
|
3265 |
}
|
3266 |
|
3267 |
+
protected function _compute_outline_color(string $val)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3268 |
{
|
3269 |
+
return $this->compute_color_value($val);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3270 |
}
|
3271 |
|
3272 |
+
protected function _compute_outline_style(string $val)
|
|
|
|
|
|
|
3273 |
{
|
3274 |
+
return \in_array($val, self::OUTLINE_STYLES, true) ? $val : null;
|
3275 |
}
|
3276 |
|
3277 |
+
protected function _compute_outline_width(string $val)
|
|
|
|
|
|
|
3278 |
{
|
3279 |
+
return $this->compute_line_width($val, "outline_style");
|
3280 |
}
|
3281 |
|
3282 |
/**
|
3283 |
+
* @link https://www.w3.org/TR/css-ui-4/#propdef-outline-offset
|
3284 |
*/
|
3285 |
+
protected function _compute_outline_offset(string $val)
|
3286 |
{
|
3287 |
+
return $this->compute_length($val);
|
3288 |
}
|
3289 |
|
3290 |
/**
|
3291 |
+
* Compute `border-spacing` to two lengths of the form
|
3292 |
+
* `[horizontal, vertical]`.
|
3293 |
*
|
3294 |
+
* @link https://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
|
|
|
3295 |
*/
|
3296 |
+
protected function _compute_border_spacing(string $val)
|
3297 |
{
|
3298 |
+
$parts = preg_split("/\s+/", $val);
|
3299 |
|
3300 |
+
if (\count($parts) > 2) {
|
3301 |
+
return null;
|
|
|
3302 |
}
|
3303 |
|
3304 |
+
$h = $this->compute_length_positive($parts[0]);
|
3305 |
+
$v = isset($parts[1])
|
3306 |
+
? $this->compute_length_positive($parts[1])
|
3307 |
+
: $h;
|
3308 |
|
3309 |
+
if ($h === null || $v === null) {
|
3310 |
+
return null;
|
3311 |
}
|
3312 |
|
3313 |
+
return [$h, $v];
|
3314 |
}
|
3315 |
|
3316 |
/**
|
3317 |
+
* @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
|
|
|
|
|
|
|
3318 |
*/
|
3319 |
+
protected function _compute_list_style_image(string $val)
|
3320 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3321 |
$parsed_val = $this->_stylesheet->resolve_url($val);
|
3322 |
|
3323 |
if ($parsed_val === "none") {
|
3324 |
+
return "none";
|
3325 |
} else {
|
3326 |
+
return "url($parsed_val)";
|
3327 |
}
|
3328 |
}
|
3329 |
|
3330 |
/**
|
3331 |
+
* @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style
|
|
|
|
|
|
|
|
|
3332 |
*/
|
3333 |
+
protected function _set_list_style(string $value): array
|
3334 |
{
|
3335 |
static $positions = ["inside", "outside"];
|
3336 |
static $types = [
|
3346 |
];
|
3347 |
|
3348 |
$components = $this->parse_property_value($value);
|
3349 |
+
$props = [];
|
3350 |
|
3351 |
foreach ($components as $val) {
|
3352 |
+
/* https://www.w3.org/TR/CSS21/generate.html#list-style
|
3353 |
* A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
|
3354 |
*/
|
3355 |
if ($val === "none") {
|
3356 |
+
$props["list_style_type"] = $val;
|
3357 |
+
$props["list_style_image"] = $val;
|
3358 |
continue;
|
3359 |
}
|
3360 |
|
3364 |
//Internet Explorer 7/8 and dompdf is right.
|
3365 |
|
3366 |
if (mb_substr($val, 0, 4) === "url(") {
|
3367 |
+
$props["list_style_image"] = $val;
|
3368 |
continue;
|
3369 |
}
|
3370 |
|
3371 |
+
if (\in_array($val, $types, true)) {
|
3372 |
+
$props["list_style_type"] = $val;
|
3373 |
+
} elseif (\in_array($val, $positions, true)) {
|
3374 |
+
$props["list_style_position"] = $val;
|
3375 |
}
|
3376 |
}
|
3377 |
+
|
3378 |
+
return $props;
|
3379 |
}
|
3380 |
|
3381 |
/**
|
3382 |
+
* @link https://www.w3.org/TR/css-page-3/#page-size-prop
|
3383 |
*/
|
3384 |
+
protected function _compute_size(string $val)
|
3385 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
3386 |
if ($val === "auto") {
|
3387 |
+
return $val;
|
|
|
3388 |
}
|
3389 |
|
3390 |
+
$parts = $this->parse_property_value($val);
|
3391 |
+
$count = \count($parts);
|
3392 |
+
|
3393 |
+
if ($count === 0 || $count > 3) {
|
3394 |
+
return null;
|
3395 |
+
}
|
3396 |
|
3397 |
+
$size = null;
|
3398 |
+
$orientation = null;
|
3399 |
+
$lengths = [];
|
3400 |
|
3401 |
+
foreach ($parts as $part) {
|
3402 |
+
if ($size === null && isset(CPDF::$PAPER_SIZES[$part])) {
|
3403 |
+
$size = $part;
|
3404 |
+
} elseif ($orientation === null && ($part === "portrait" || $part === "landscape")) {
|
3405 |
+
$orientation = $part;
|
3406 |
} else {
|
3407 |
+
$lengths[] = $part;
|
3408 |
}
|
3409 |
+
}
|
3410 |
|
3411 |
+
if ($size !== null && $lengths !== []) {
|
3412 |
+
return null;
|
3413 |
+
}
|
|
|
|
|
3414 |
|
3415 |
+
if ($size !== null) {
|
3416 |
+
// Standard paper size
|
3417 |
+
[$l1, $l2] = \array_slice(CPDF::$PAPER_SIZES[$size], 2, 2);
|
3418 |
+
} elseif ($lengths === []) {
|
3419 |
+
// Orientation only, use default paper size
|
3420 |
+
$dims = $this->_stylesheet->get_dompdf()->getPaperSize();
|
3421 |
+
[$l1, $l2] = \array_slice($dims, 2, 2);
|
3422 |
} else {
|
3423 |
+
// Custom paper size
|
3424 |
+
$l1 = $this->compute_length_positive($lengths[0]);
|
3425 |
+
$l2 = isset($lengths[1]) ? $this->compute_length_positive($lengths[1]) : $l1;
|
3426 |
+
|
3427 |
+
if ($l1 === null || $l2 === null) {
|
3428 |
+
return null;
|
3429 |
+
}
|
3430 |
}
|
3431 |
|
3432 |
+
if (($orientation === "portrait" && $l1 > $l2)
|
3433 |
+
|| ($orientation === "landscape" && $l2 > $l1)
|
3434 |
+
) {
|
3435 |
+
return [$l2, $l1];
|
3436 |
+
}
|
3437 |
+
|
3438 |
+
return [$l1, $l2];
|
3439 |
}
|
3440 |
|
3441 |
/**
|
3442 |
+
* @param string $computed
|
3443 |
+
* @return array
|
3444 |
*
|
3445 |
+
* @link https://www.w3.org/TR/css-transforms-1/#transform-property
|
|
|
3446 |
*/
|
3447 |
+
protected function _get_transform($computed)
|
3448 |
{
|
3449 |
//TODO: should be handled in setter (lengths set to absolute)
|
3450 |
|
3452 |
$tr_value = "\s*([^,\s]+)\s*";
|
3453 |
$angle = "\s*([^,\s]+(?:deg|rad)?)\s*";
|
3454 |
|
3455 |
+
if (!preg_match_all("/[a-z]+\([^\)]+\)/i", $computed, $parts, PREG_SET_ORDER)) {
|
3456 |
+
return [];
|
3457 |
}
|
3458 |
|
3459 |
$functions = [
|
3481 |
|
3482 |
foreach ($functions as $name => $pattern) {
|
3483 |
if (preg_match("/$name\s*$pattern/i", $t, $matches)) {
|
3484 |
+
$values = \array_slice($matches, 1);
|
3485 |
|
3486 |
switch ($name) {
|
3487 |
// <angle> units
|
3492 |
|
3493 |
foreach ($values as $i => $value) {
|
3494 |
if (strpos($value, "rad")) {
|
3495 |
+
$values[$i] = rad2deg((float) $value);
|
3496 |
} else {
|
3497 |
+
$values[$i] = (float) $value;
|
3498 |
}
|
3499 |
}
|
3500 |
|
3566 |
}
|
3567 |
|
3568 |
/**
|
3569 |
+
* @param string $computed
|
3570 |
+
* @return array
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3571 |
*
|
3572 |
+
* @link https://www.w3.org/TR/css-transforms-1/#transform-origin-property
|
|
|
3573 |
*/
|
3574 |
+
protected function _get_transform_origin($computed)
|
3575 |
{
|
3576 |
//TODO: should be handled in setter
|
3577 |
+
|
3578 |
+
$values = preg_split("/\s+/", $computed);
|
3579 |
|
3580 |
$values = array_map(function ($value) {
|
3581 |
+
if (\in_array($value, ["top", "left"], true)) {
|
3582 |
return 0;
|
3583 |
+
} elseif (\in_array($value, ["bottom", "right"], true)) {
|
3584 |
return "100%";
|
3585 |
} else {
|
3586 |
return $value;
|
3595 |
}
|
3596 |
|
3597 |
/**
|
3598 |
+
* @param string $val
|
3599 |
+
* @return string|null
|
3600 |
*/
|
3601 |
+
protected function parse_image_resolution(string $val): ?string
|
3602 |
{
|
3603 |
// If exif data could be get:
|
3604 |
// $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';
|
3614 |
|
3615 |
/**
|
3616 |
* auto | normal | dpi
|
|
|
|
|
3617 |
*/
|
3618 |
+
protected function _compute_background_image_resolution(string $val)
|
3619 |
{
|
3620 |
+
return $this->parse_image_resolution($val);
|
3621 |
+
}
|
3622 |
|
3623 |
+
/**
|
3624 |
+
* auto | normal | dpi
|
3625 |
+
*/
|
3626 |
+
protected function _compute_image_resolution(string $val)
|
3627 |
+
{
|
3628 |
+
return $this->parse_image_resolution($val);
|
3629 |
+
}
|
3630 |
|
3631 |
+
/**
|
3632 |
+
* @link https://www.w3.org/TR/css-break-3/#propdef-orphans
|
3633 |
+
*/
|
3634 |
+
protected function _compute_orphans(string $val)
|
3635 |
+
{
|
3636 |
+
return $this->compute_integer($val);
|
3637 |
+
}
|
3638 |
|
3639 |
+
/**
|
3640 |
+
* @link https://www.w3.org/TR/css-break-3/#propdef-widows
|
3641 |
+
*/
|
3642 |
+
protected function _compute_widows(string $val)
|
3643 |
+
{
|
3644 |
+
return $this->compute_integer($val);
|
3645 |
}
|
3646 |
|
3647 |
/**
|
3648 |
+
* @link https://www.w3.org/TR/css-color-4/#propdef-opacity
|
|
|
|
|
3649 |
*/
|
3650 |
+
protected function _compute_opacity(string $val)
|
3651 |
{
|
3652 |
+
$number = self::CSS_NUMBER;
|
3653 |
+
$pattern = "/^($number)(%?)$/";
|
3654 |
|
3655 |
+
if (!preg_match($pattern, $val, $matches)) {
|
3656 |
+
return null;
|
|
|
3657 |
}
|
3658 |
|
3659 |
+
$v = (float) $matches[1];
|
3660 |
+
$percent = $matches[2] === "%";
|
3661 |
+
$opacity = $percent ? ($v / 100) : $v;
|
3662 |
|
3663 |
+
return max(0.0, min($opacity, 1.0));
|
3664 |
}
|
3665 |
|
3666 |
/**
|
3667 |
+
* @link https://www.w3.org/TR/CSS21//visuren.html#propdef-z-index
|
3668 |
*/
|
3669 |
+
protected function _compute_z_index(string $val)
|
3670 |
{
|
3671 |
+
if ($val === "auto") {
|
3672 |
+
return $val;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3673 |
}
|
3674 |
|
3675 |
+
return $this->compute_integer($val);
|
3676 |
}
|
3677 |
|
3678 |
/**
|
3702 |
* @return string
|
3703 |
*/
|
3704 |
/*DEBUGCSS print: see below additional debugging util*/
|
3705 |
+
public function __toString(): string
|
3706 |
{
|
3707 |
$parent_font_size = $this->parent_style
|
3708 |
? $this->parent_style->font_size
|
3709 |
: self::$default_font_size;
|
3710 |
|
3711 |
+
return print_r(array_merge(["parent_font_size" => $parent_font_size],
|
3712 |
$this->_props), true);
|
3713 |
}
|
3714 |
|
3715 |
/*DEBUGCSS*/
|
3716 |
+
public function debug_print(): void
|
3717 |
{
|
3718 |
$parent_font_size = $this->parent_style
|
3719 |
? $this->parent_style->font_size
|
3737 |
}
|
3738 |
print " ]\n";
|
3739 |
print " cached [\n";
|
3740 |
+
foreach ($this->_props_used as $prop => $val) {
|
3741 |
print ' ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true));
|
3742 |
print ";\n";
|
3743 |
}
|
vendor/dompdf/dompdf/src/Css/Stylesheet.php
CHANGED
@@ -288,19 +288,6 @@ class Stylesheet
|
|
288 |
$this->_styles[$key][] = $style;
|
289 |
}
|
290 |
|
291 |
-
/**
|
292 |
-
* Lookup a specific Style collection
|
293 |
-
*
|
294 |
-
* @deprecated
|
295 |
-
* @param string $key the selector of the requested Style collection
|
296 |
-
*
|
297 |
-
* @return Style[]
|
298 |
-
*/
|
299 |
-
function lookup(string $key): array
|
300 |
-
{
|
301 |
-
return $this->_styles[$key] ?? [];
|
302 |
-
}
|
303 |
-
|
304 |
/**
|
305 |
* load and parse a CSS string
|
306 |
*
|
@@ -338,46 +325,26 @@ class Stylesheet
|
|
338 |
$parsed = Helpers::parse_data_uri($file);
|
339 |
$css = $parsed["data"];
|
340 |
} else {
|
341 |
-
$
|
342 |
-
|
343 |
-
[$this->_protocol, $this->_base_host, $this->_base_path, $filename] = $parsed_url;
|
344 |
|
345 |
-
$
|
|
|
346 |
|
347 |
-
$
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
if (strpos($realfile, $rootDir) !== 0) {
|
358 |
-
$chroot = $options->getChroot();
|
359 |
-
$chrootValid = false;
|
360 |
-
foreach ($chroot as $chrootPath) {
|
361 |
-
$chrootPath = realpath($chrootPath);
|
362 |
-
if ($chrootPath !== false && strpos($realfile, $chrootPath) === 0) {
|
363 |
-
$chrootValid = true;
|
364 |
-
break;
|
365 |
-
}
|
366 |
-
}
|
367 |
-
if ($chrootValid !== true) {
|
368 |
-
Helpers::record_warnings(E_USER_WARNING, "Permission denied on $file. The file could not be found under the paths specified by Options::chroot.", __FILE__, __LINE__);
|
369 |
return;
|
370 |
}
|
371 |
}
|
372 |
-
|
373 |
-
if (!$realfile) {
|
374 |
-
Helpers::record_warnings(E_USER_WARNING, "File '$realfile' not found.", __FILE__, __LINE__);
|
375 |
-
return;
|
376 |
-
}
|
377 |
-
|
378 |
-
$file = $realfile;
|
379 |
}
|
380 |
-
|
381 |
[$css, $http_response_header] = Helpers::getFileContent($file, $this->_dompdf->getHttpContext());
|
382 |
|
383 |
$good_mime_type = true;
|
@@ -392,11 +359,12 @@ class Stylesheet
|
|
392 |
}
|
393 |
}
|
394 |
}
|
395 |
-
|
396 |
if (!$good_mime_type || $css === null) {
|
397 |
Helpers::record_warnings(E_USER_WARNING, "Unable to load css file $file", __FILE__, __LINE__);
|
398 |
return;
|
399 |
}
|
|
|
|
|
400 |
}
|
401 |
|
402 |
$this->_parse_css($css);
|
@@ -577,7 +545,7 @@ class Stylesheet
|
|
577 |
// class=".* $tok .*" and class=".* $tok"
|
578 |
|
579 |
// This doesn't work because libxml only supports XPath 1.0...
|
580 |
-
//$query .= "[matches(@$attr,\"
|
581 |
|
582 |
$query .= "[contains(concat(' ', normalize-space(@$attr), ' '), concat(' ', '$tok', ' '))]";
|
583 |
$tok = "";
|
@@ -620,7 +588,7 @@ class Stylesheet
|
|
620 |
switch ($tok) {
|
621 |
|
622 |
case "first-child":
|
623 |
-
$query .= "[
|
624 |
$tok = "";
|
625 |
break;
|
626 |
|
@@ -653,16 +621,17 @@ class Stylesheet
|
|
653 |
$pseudo_classes[$tok] = true;
|
654 |
$p = $i + 1;
|
655 |
$nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i) - $p));
|
|
|
656 |
|
657 |
// 1
|
658 |
if (preg_match("/^\d+$/", $nth)) {
|
659 |
-
$condition = "position
|
660 |
} // odd
|
661 |
elseif ($nth === "odd") {
|
662 |
-
$condition = "(position
|
663 |
} // even
|
664 |
elseif ($nth === "even") {
|
665 |
-
$condition = "(position
|
666 |
} // an+b
|
667 |
else {
|
668 |
$condition = $this->_selector_an_plus_b($nth, $last);
|
@@ -684,16 +653,17 @@ class Stylesheet
|
|
684 |
$pseudo_classes[$tok] = true;
|
685 |
$p = $i + 1;
|
686 |
$nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i) - $p));
|
|
|
687 |
|
688 |
// 1
|
689 |
if (preg_match("/^\d+$/", $nth)) {
|
690 |
-
$condition = "position
|
691 |
} // odd
|
692 |
elseif ($nth === "odd") {
|
693 |
-
$condition = "(position
|
694 |
} // even
|
695 |
elseif ($nth === "even") {
|
696 |
-
$condition = "(position
|
697 |
} // an+b
|
698 |
else {
|
699 |
$condition = $this->_selector_an_plus_b($nth, $last);
|
@@ -920,26 +890,27 @@ class Stylesheet
|
|
920 |
/**
|
921 |
* https://github.com/tenderlove/nokogiri/blob/master/lib/nokogiri/css/xpath_visitor.rb
|
922 |
*
|
923 |
-
* @param $expr
|
924 |
* @param bool $last
|
|
|
925 |
* @return string
|
926 |
*/
|
927 |
-
protected function _selector_an_plus_b($expr, $last = false)
|
928 |
{
|
929 |
$expr = preg_replace("/\s/", "", $expr);
|
930 |
if (!preg_match("/^(?P<a>-?[0-9]*)?n(?P<b>[-+]?[0-9]+)?$/", $expr, $matches)) {
|
931 |
return "false()";
|
932 |
}
|
933 |
|
934 |
-
$a = (
|
935 |
-
$b = (
|
936 |
|
937 |
-
$position =
|
938 |
|
939 |
if ($b == 0) {
|
940 |
return "($position mod $a) = 0";
|
941 |
} else {
|
942 |
-
$compare = (
|
943 |
$b2 = -$b;
|
944 |
if ($b2 >= 0) {
|
945 |
$b2 = "+$b2";
|
@@ -1006,7 +977,7 @@ class Stylesheet
|
|
1006 |
continue;
|
1007 |
}
|
1008 |
|
1009 |
-
$content = $style->
|
1010 |
|
1011 |
// Do not create non-displayed before/after pseudo elements
|
1012 |
// https://www.w3.org/TR/CSS21/generate.html#content
|
@@ -1075,7 +1046,7 @@ class Stylesheet
|
|
1075 |
// Now create the styles and assign them to the appropriate frames. (We
|
1076 |
// iterate over the tree using an implicit FrameTree iterator.)
|
1077 |
$root_flg = false;
|
1078 |
-
foreach ($tree
|
1079 |
// Helpers::pre_r($frame->get_node()->nodeName . ":");
|
1080 |
if (!$root_flg && $this->_page_styles["base"]) {
|
1081 |
$style = $this->_page_styles["base"];
|
@@ -1330,7 +1301,7 @@ class Stylesheet
|
|
1330 |
$media_query_feature = strtolower($media_query_match[3]);
|
1331 |
$media_query_value = strtolower($media_query_match[2]);
|
1332 |
$mq[] = [$media_query_feature, $media_query_value];
|
1333 |
-
}
|
1334 |
$media_query_feature = strtolower($media_query_match[5]);
|
1335 |
$media_query_value = (array_key_exists(8, $media_query_match) ? strtolower($media_query_match[8]) : null);
|
1336 |
$mq[] = [$media_query_feature, $media_query_value];
|
@@ -1431,20 +1402,16 @@ class Stylesheet
|
|
1431 |
$val = preg_replace("/url\(\s*['\"]?([^'\")]+)['\"]?\s*\)/", "\\1", trim($val));
|
1432 |
|
1433 |
// Resolve the url now in the context of the current stylesheet
|
1434 |
-
$parsed_url = Helpers::explode_url($val);
|
1435 |
$path = Helpers::build_url($this->_protocol,
|
1436 |
$this->_base_host,
|
1437 |
$this->_base_path,
|
1438 |
$val);
|
1439 |
-
if (
|
1440 |
-
$path =
|
1441 |
-
// If realpath returns FALSE then specifically state that there is no background image
|
1442 |
-
if ($path === false) {
|
1443 |
-
$path = "none";
|
1444 |
-
}
|
1445 |
}
|
1446 |
}
|
1447 |
if ($DEBUGCSS) {
|
|
|
1448 |
print "<pre>[_image\n";
|
1449 |
print_r($parsed_url);
|
1450 |
print $this->_protocol . "\n" . $this->_base_path . "\n" . $path . "\n";
|
@@ -1493,9 +1460,9 @@ class Stylesheet
|
|
1493 |
// Above does not work for subfolders and absolute urls.
|
1494 |
// Todo: As above, do we need to replace php or file to an empty protocol for local files?
|
1495 |
|
1496 |
-
$url = $this->resolve_url($url)
|
1497 |
-
|
1498 |
-
|
1499 |
|
1500 |
// Restore the current base url
|
1501 |
$this->_protocol = $protocol;
|
@@ -1516,9 +1483,7 @@ class Stylesheet
|
|
1516 |
|
1517 |
preg_match_all("/(url|local)\s*\([\"\']?([^\"\'\)]+)[\"\']?\)\s*(format\s*\([\"\']?([^\"\'\)]+)[\"\']?\))?/i", $descriptors->src, $src);
|
1518 |
|
1519 |
-
$sources = [];
|
1520 |
$valid_sources = [];
|
1521 |
-
|
1522 |
foreach ($src[0] as $i => $value) {
|
1523 |
$source = [
|
1524 |
"local" => strtolower($src[1][$i]) === "local",
|
@@ -1527,11 +1492,9 @@ class Stylesheet
|
|
1527 |
"path" => Helpers::build_url($this->_protocol, $this->_base_host, $this->_base_path, $src[2][$i]),
|
1528 |
];
|
1529 |
|
1530 |
-
if (!$source["local"] && in_array($source["format"], ["", "truetype"])) {
|
1531 |
$valid_sources[] = $source;
|
1532 |
}
|
1533 |
-
|
1534 |
-
$sources[] = $source;
|
1535 |
}
|
1536 |
|
1537 |
// No valid sources
|
@@ -1685,7 +1648,7 @@ class Stylesheet
|
|
1685 |
{
|
1686 |
$options = $this->_dompdf->getOptions();
|
1687 |
$rootDir = realpath($options->getRootDir());
|
1688 |
-
return $rootDir . self::DEFAULT_STYLESHEET;
|
1689 |
}
|
1690 |
|
1691 |
/**
|
288 |
$this->_styles[$key][] = $style;
|
289 |
}
|
290 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
/**
|
292 |
* load and parse a CSS string
|
293 |
*
|
325 |
$parsed = Helpers::parse_data_uri($file);
|
326 |
$css = $parsed["data"];
|
327 |
} else {
|
328 |
+
$options = $this->_dompdf->getOptions();
|
|
|
|
|
329 |
|
330 |
+
$parsed_url = Helpers::explode_url($file);
|
331 |
+
$protocol = $parsed_url["protocol"];
|
332 |
|
333 |
+
if ($file !== $this->getDefaultStylesheet()) {
|
334 |
+
$allowed_protocols = $options->getAllowedProtocols();
|
335 |
+
if (!array_key_exists($protocol, $allowed_protocols)) {
|
336 |
+
Helpers::record_warnings(E_USER_WARNING, "Permission denied on $file. The communication protocol is not supported.", __FILE__, __LINE__);
|
337 |
+
return;
|
338 |
+
}
|
339 |
+
foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
|
340 |
+
[$result, $message] = $rule($file);
|
341 |
+
if (!$result) {
|
342 |
+
Helpers::record_warnings(E_USER_WARNING, "Error loading $file: $message", __FILE__, __LINE__);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
343 |
return;
|
344 |
}
|
345 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
346 |
}
|
347 |
+
|
348 |
[$css, $http_response_header] = Helpers::getFileContent($file, $this->_dompdf->getHttpContext());
|
349 |
|
350 |
$good_mime_type = true;
|
359 |
}
|
360 |
}
|
361 |
}
|
|
|
362 |
if (!$good_mime_type || $css === null) {
|
363 |
Helpers::record_warnings(E_USER_WARNING, "Unable to load css file $file", __FILE__, __LINE__);
|
364 |
return;
|
365 |
}
|
366 |
+
|
367 |
+
[$this->_protocol, $this->_base_host, $this->_base_path] = $parsed_url;
|
368 |
}
|
369 |
|
370 |
$this->_parse_css($css);
|
545 |
// class=".* $tok .*" and class=".* $tok"
|
546 |
|
547 |
// This doesn't work because libxml only supports XPath 1.0...
|
548 |
+
//$query .= "[matches(@$attr,\"^{$tok}\$|^{$tok}[ ]+|[ ]+{$tok}\$|[ ]+{$tok}[ ]+\")]";
|
549 |
|
550 |
$query .= "[contains(concat(' ', normalize-space(@$attr), ' '), concat(' ', '$tok', ' '))]";
|
551 |
$tok = "";
|
588 |
switch ($tok) {
|
589 |
|
590 |
case "first-child":
|
591 |
+
$query .= "[not(preceding-sibling::*)]";
|
592 |
$tok = "";
|
593 |
break;
|
594 |
|
621 |
$pseudo_classes[$tok] = true;
|
622 |
$p = $i + 1;
|
623 |
$nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i) - $p));
|
624 |
+
$position = $last ? "(last()-position()+1)" : "position()";
|
625 |
|
626 |
// 1
|
627 |
if (preg_match("/^\d+$/", $nth)) {
|
628 |
+
$condition = "$position = $nth";
|
629 |
} // odd
|
630 |
elseif ($nth === "odd") {
|
631 |
+
$condition = "($position mod 2) = 1";
|
632 |
} // even
|
633 |
elseif ($nth === "even") {
|
634 |
+
$condition = "($position mod 2) = 0";
|
635 |
} // an+b
|
636 |
else {
|
637 |
$condition = $this->_selector_an_plus_b($nth, $last);
|
653 |
$pseudo_classes[$tok] = true;
|
654 |
$p = $i + 1;
|
655 |
$nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i) - $p));
|
656 |
+
$position = $last ? "(last()-position()+1)" : "position()";
|
657 |
|
658 |
// 1
|
659 |
if (preg_match("/^\d+$/", $nth)) {
|
660 |
+
$condition = "$position = $nth";
|
661 |
} // odd
|
662 |
elseif ($nth === "odd") {
|
663 |
+
$condition = "($position mod 2) = 1";
|
664 |
} // even
|
665 |
elseif ($nth === "even") {
|
666 |
+
$condition = "($position mod 2) = 0";
|
667 |
} // an+b
|
668 |
else {
|
669 |
$condition = $this->_selector_an_plus_b($nth, $last);
|
890 |
/**
|
891 |
* https://github.com/tenderlove/nokogiri/blob/master/lib/nokogiri/css/xpath_visitor.rb
|
892 |
*
|
893 |
+
* @param string $expr
|
894 |
* @param bool $last
|
895 |
+
*
|
896 |
* @return string
|
897 |
*/
|
898 |
+
protected function _selector_an_plus_b(string $expr, bool $last = false): string
|
899 |
{
|
900 |
$expr = preg_replace("/\s/", "", $expr);
|
901 |
if (!preg_match("/^(?P<a>-?[0-9]*)?n(?P<b>[-+]?[0-9]+)?$/", $expr, $matches)) {
|
902 |
return "false()";
|
903 |
}
|
904 |
|
905 |
+
$a = (isset($matches["a"]) && $matches["a"] !== "") ? ($matches["a"] !== "-" ? intval($matches["a"]) : -1) : 1;
|
906 |
+
$b = (isset($matches["b"]) && $matches["b"] !== "") ? intval($matches["b"]) : 0;
|
907 |
|
908 |
+
$position = $last ? "(last()-position()+1)" : "position()";
|
909 |
|
910 |
if ($b == 0) {
|
911 |
return "($position mod $a) = 0";
|
912 |
} else {
|
913 |
+
$compare = ($a < 0) ? "<=" : ">=";
|
914 |
$b2 = -$b;
|
915 |
if ($b2 >= 0) {
|
916 |
$b2 = "+$b2";
|
977 |
continue;
|
978 |
}
|
979 |
|
980 |
+
$content = $style->get_specified("content");
|
981 |
|
982 |
// Do not create non-displayed before/after pseudo elements
|
983 |
// https://www.w3.org/TR/CSS21/generate.html#content
|
1046 |
// Now create the styles and assign them to the appropriate frames. (We
|
1047 |
// iterate over the tree using an implicit FrameTree iterator.)
|
1048 |
$root_flg = false;
|
1049 |
+
foreach ($tree as $frame) {
|
1050 |
// Helpers::pre_r($frame->get_node()->nodeName . ":");
|
1051 |
if (!$root_flg && $this->_page_styles["base"]) {
|
1052 |
$style = $this->_page_styles["base"];
|
1301 |
$media_query_feature = strtolower($media_query_match[3]);
|
1302 |
$media_query_value = strtolower($media_query_match[2]);
|
1303 |
$mq[] = [$media_query_feature, $media_query_value];
|
1304 |
+
} elseif (empty($media_query_match[4]) === false) {
|
1305 |
$media_query_feature = strtolower($media_query_match[5]);
|
1306 |
$media_query_value = (array_key_exists(8, $media_query_match) ? strtolower($media_query_match[8]) : null);
|
1307 |
$mq[] = [$media_query_feature, $media_query_value];
|
1402 |
$val = preg_replace("/url\(\s*['\"]?([^'\")]+)['\"]?\s*\)/", "\\1", trim($val));
|
1403 |
|
1404 |
// Resolve the url now in the context of the current stylesheet
|
|
|
1405 |
$path = Helpers::build_url($this->_protocol,
|
1406 |
$this->_base_host,
|
1407 |
$this->_base_path,
|
1408 |
$val);
|
1409 |
+
if ($path === null) {
|
1410 |
+
$path = "none";
|
|
|
|
|
|
|
|
|
1411 |
}
|
1412 |
}
|
1413 |
if ($DEBUGCSS) {
|
1414 |
+
$parsed_url = Helpers::explode_url($path);
|
1415 |
print "<pre>[_image\n";
|
1416 |
print_r($parsed_url);
|
1417 |
print $this->_protocol . "\n" . $this->_base_path . "\n" . $path . "\n";
|
1460 |
// Above does not work for subfolders and absolute urls.
|
1461 |
// Todo: As above, do we need to replace php or file to an empty protocol for local files?
|
1462 |
|
1463 |
+
if (($url = $this->resolve_url($url)) !== "none") {
|
1464 |
+
$this->load_css_file($url);
|
1465 |
+
}
|
1466 |
|
1467 |
// Restore the current base url
|
1468 |
$this->_protocol = $protocol;
|
1483 |
|
1484 |
preg_match_all("/(url|local)\s*\([\"\']?([^\"\'\)]+)[\"\']?\)\s*(format\s*\([\"\']?([^\"\'\)]+)[\"\']?\))?/i", $descriptors->src, $src);
|
1485 |
|
|
|
1486 |
$valid_sources = [];
|
|
|
1487 |
foreach ($src[0] as $i => $value) {
|
1488 |
$source = [
|
1489 |
"local" => strtolower($src[1][$i]) === "local",
|
1492 |
"path" => Helpers::build_url($this->_protocol, $this->_base_host, $this->_base_path, $src[2][$i]),
|
1493 |
];
|
1494 |
|
1495 |
+
if (!$source["local"] && in_array($source["format"], ["", "truetype"]) && $source["path"] !== null) {
|
1496 |
$valid_sources[] = $source;
|
1497 |
}
|
|
|
|
|
1498 |
}
|
1499 |
|
1500 |
// No valid sources
|
1648 |
{
|
1649 |
$options = $this->_dompdf->getOptions();
|
1650 |
$rootDir = realpath($options->getRootDir());
|
1651 |
+
return Helpers::build_url("file://", "", $rootDir, $rootDir . self::DEFAULT_STYLESHEET);
|
1652 |
}
|
1653 |
|
1654 |
/**
|
vendor/dompdf/dompdf/src/Dompdf.php
CHANGED
@@ -14,11 +14,10 @@ use Dompdf\Adapter\CPDF;
|
|
14 |
use DOMXPath;
|
15 |
use Dompdf\Frame\Factory;
|
16 |
use Dompdf\Frame\FrameTree;
|
17 |
-
use HTML5_Tokenizer;
|
18 |
-
use HTML5_TreeBuilder;
|
19 |
use Dompdf\Image\Cache;
|
20 |
use Dompdf\Css\Stylesheet;
|
21 |
use Dompdf\Helpers;
|
|
|
22 |
|
23 |
/**
|
24 |
* Dompdf - PHP5 HTML to PDF renderer
|
@@ -197,16 +196,6 @@ class Dompdf
|
|
197 |
*/
|
198 |
private $quirksmode = false;
|
199 |
|
200 |
-
/**
|
201 |
-
* Protocol whitelist
|
202 |
-
*
|
203 |
-
* Protocols and PHP wrappers allowed in URLs. Full support is not
|
204 |
-
* guaranteed for the protocols/wrappers contained in this array.
|
205 |
-
*
|
206 |
-
* @var array
|
207 |
-
*/
|
208 |
-
private $allowedProtocols = ["", "file://", "http://", "https://"];
|
209 |
-
|
210 |
/**
|
211 |
* Local file extension whitelist
|
212 |
*
|
@@ -259,7 +248,7 @@ class Dompdf
|
|
259 |
/**
|
260 |
* Class constructor
|
261 |
*
|
262 |
-
* @param array|
|
263 |
*/
|
264 |
public function __construct($options = null)
|
265 |
{
|
@@ -272,8 +261,11 @@ class Dompdf
|
|
272 |
}
|
273 |
|
274 |
$versionFile = realpath(__DIR__ . '/../VERSION');
|
275 |
-
if (
|
276 |
-
$
|
|
|
|
|
|
|
277 |
}
|
278 |
|
279 |
$this->setPhpConfig();
|
@@ -281,8 +273,8 @@ class Dompdf
|
|
281 |
$this->paperSize = $this->options->getDefaultPaperSize();
|
282 |
$this->paperOrientation = $this->options->getDefaultPaperOrientation();
|
283 |
|
284 |
-
$this->
|
285 |
-
$this->
|
286 |
$this->css = new Stylesheet($this);
|
287 |
|
288 |
$this->restorePhpConfig();
|
@@ -353,43 +345,25 @@ class Dompdf
|
|
353 |
[$this->protocol, $this->baseHost, $this->basePath] = Helpers::explode_url($file);
|
354 |
}
|
355 |
$protocol = strtolower($this->protocol);
|
356 |
-
|
357 |
$uri = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $file);
|
358 |
|
359 |
-
|
|
|
360 |
throw new Exception("Permission denied on $file. The communication protocol is not supported.");
|
361 |
}
|
362 |
|
363 |
-
if (
|
364 |
-
|
365 |
-
}
|
366 |
-
|
367 |
-
if ($protocol === "" || $protocol === "file://") {
|
368 |
-
$realfile = realpath($uri);
|
369 |
-
|
370 |
-
$chroot = $this->options->getChroot();
|
371 |
-
$chrootValid = false;
|
372 |
-
foreach ($chroot as $chrootPath) {
|
373 |
-
$chrootPath = realpath($chrootPath);
|
374 |
-
if ($chrootPath !== false && strpos($realfile, $chrootPath) === 0) {
|
375 |
-
$chrootValid = true;
|
376 |
-
break;
|
377 |
-
}
|
378 |
-
}
|
379 |
-
if ($chrootValid !== true) {
|
380 |
-
throw new Exception("Permission denied on $file. The file could not be found under the paths specified by Options::chroot.");
|
381 |
-
}
|
382 |
-
|
383 |
-
$ext = strtolower(pathinfo($realfile, PATHINFO_EXTENSION));
|
384 |
if (!in_array($ext, $this->allowedLocalFileExtensions)) {
|
385 |
-
throw new Exception("Permission denied on $file
|
386 |
}
|
|
|
387 |
|
388 |
-
|
389 |
-
|
|
|
|
|
390 |
}
|
391 |
-
|
392 |
-
$uri = $realfile;
|
393 |
}
|
394 |
|
395 |
[$contents, $http_response_header] = Helpers::getFileContent($uri, $this->options->getHttpContext());
|
@@ -498,43 +472,18 @@ class Dompdf
|
|
498 |
|
499 |
try {
|
500 |
// @todo Take the quirksmode into account
|
|
|
501 |
// http://hsivonen.iki.fi/doctype/
|
502 |
-
// https://developer.mozilla.org/en/mozilla's_quirks_mode
|
503 |
$quirksmode = false;
|
504 |
|
505 |
-
|
506 |
-
|
507 |
-
$tokenizer->parse();
|
508 |
-
$doc = $tokenizer->save();
|
509 |
-
|
510 |
-
$quirksmode = ($tokenizer->getTree()->getQuirksMode() > HTML5_TreeBuilder::NO_QUIRKS);
|
511 |
-
} else {
|
512 |
-
// loadHTML assumes ISO-8859-1 unless otherwise specified on the HTML document header.
|
513 |
-
// http://devzone.zend.com/1538/php-dom-xml-extension-encoding-processing/ (see #4)
|
514 |
-
// http://stackoverflow.com/a/11310258/264628
|
515 |
-
$doc = new DOMDocument("1.0", $encoding);
|
516 |
-
$doc->preserveWhiteSpace = true;
|
517 |
-
$doc->loadHTML($str);
|
518 |
-
$doc->encoding = $encoding;
|
519 |
-
|
520 |
-
// If some text is before the doctype, we are in quirksmode
|
521 |
-
if (preg_match("/^(.+)<!doctype/i", ltrim($str), $matches)) {
|
522 |
-
$quirksmode = true;
|
523 |
-
} // If no doctype is provided, we are in quirksmode
|
524 |
-
elseif (!preg_match("/^<!doctype/i", ltrim($str), $matches)) {
|
525 |
-
$quirksmode = true;
|
526 |
-
} else {
|
527 |
-
// HTML5 <!DOCTYPE html>
|
528 |
-
if (!$doc->doctype->publicId && !$doc->doctype->systemId) {
|
529 |
-
$quirksmode = false;
|
530 |
-
}
|
531 |
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
}
|
538 |
|
539 |
$this->loadDOM($doc, $quirksmode);
|
540 |
} finally {
|
@@ -584,9 +533,11 @@ class Dompdf
|
|
584 |
$acceptedmedia[] = $this->options->getDefaultMediaType();
|
585 |
|
586 |
// <base href="" />
|
587 |
-
|
588 |
-
|
589 |
-
|
|
|
|
|
590 |
}
|
591 |
|
592 |
// Set the base path of the Stylesheet to that of the file being processed
|
@@ -628,7 +579,9 @@ class Dompdf
|
|
628 |
$url = $tag->getAttribute("href");
|
629 |
$url = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $url);
|
630 |
|
631 |
-
|
|
|
|
|
632 |
}
|
633 |
break;
|
634 |
|
@@ -726,9 +679,8 @@ class Dompdf
|
|
726 |
public function render()
|
727 |
{
|
728 |
$this->setPhpConfig();
|
729 |
-
$options = $this->options;
|
730 |
|
731 |
-
$logOutputFile = $options->getLogOutputFile();
|
732 |
if ($logOutputFile) {
|
733 |
if (!file_exists($logOutputFile) && is_writable(dirname($logOutputFile))) {
|
734 |
touch($logOutputFile);
|
@@ -753,29 +705,27 @@ class Dompdf
|
|
753 |
$pageStyle->inherit($basePageStyle);
|
754 |
}
|
755 |
|
756 |
-
|
757 |
-
// If there is a CSS defined paper size compare to the paper size used to create the canvas to determine a
|
758 |
-
// recreation need
|
759 |
if (is_array($basePageStyle->size)) {
|
760 |
-
$
|
761 |
-
$this->setPaper([0, 0, $
|
762 |
}
|
763 |
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
) {
|
770 |
-
$this->setCanvas(CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation));
|
771 |
-
$this->fontMetrics->setCanvas($this->getCanvas());
|
772 |
-
}
|
773 |
|
774 |
-
$
|
|
|
|
|
|
|
775 |
|
|
|
776 |
$root = null;
|
777 |
|
778 |
-
foreach ($this->tree
|
779 |
// Set up the root frame
|
780 |
if (is_null($root)) {
|
781 |
$root = Factory::decorate_root($this->tree->get_root(), $this);
|
@@ -819,6 +769,14 @@ class Dompdf
|
|
819 |
// This is where the magic happens:
|
820 |
$root->reflow();
|
821 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
822 |
// Clean up cached images
|
823 |
if (!$this->options->getDebugKeepTemp()) {
|
824 |
Cache::clear($this->options->getDebugPng());
|
@@ -846,17 +804,6 @@ class Dompdf
|
|
846 |
$this->restorePhpConfig();
|
847 |
}
|
848 |
|
849 |
-
/**
|
850 |
-
* Add meta information to the PDF after rendering
|
851 |
-
*/
|
852 |
-
public function add_info($label, $value)
|
853 |
-
{
|
854 |
-
$canvas = $this->getCanvas();
|
855 |
-
if (!is_null($canvas)) {
|
856 |
-
$canvas->add_info($label, $value);
|
857 |
-
}
|
858 |
-
}
|
859 |
-
|
860 |
/**
|
861 |
* Writes the output buffer in the log file
|
862 |
*
|
@@ -883,6 +830,27 @@ class Dompdf
|
|
883 |
file_put_contents($logOutputFile, $out);
|
884 |
}
|
885 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
886 |
/**
|
887 |
* Streams the PDF to the client.
|
888 |
*
|
@@ -903,10 +871,7 @@ class Dompdf
|
|
903 |
{
|
904 |
$this->setPhpConfig();
|
905 |
|
906 |
-
$canvas
|
907 |
-
if (!is_null($canvas)) {
|
908 |
-
$canvas->stream($filename, $options);
|
909 |
-
}
|
910 |
|
911 |
$this->restorePhpConfig();
|
912 |
}
|
@@ -927,12 +892,7 @@ class Dompdf
|
|
927 |
{
|
928 |
$this->setPhpConfig();
|
929 |
|
930 |
-
$
|
931 |
-
if (is_null($canvas)) {
|
932 |
-
return null;
|
933 |
-
}
|
934 |
-
|
935 |
-
$output = $canvas->output($options);
|
936 |
|
937 |
$this->restorePhpConfig();
|
938 |
|
@@ -1006,7 +966,7 @@ class Dompdf
|
|
1006 |
/**
|
1007 |
* Sets the paper size & orientation
|
1008 |
*
|
1009 |
-
* @param string|
|
1010 |
* @param string $orientation 'portrait' or 'landscape'
|
1011 |
* @return $this
|
1012 |
*/
|
@@ -1020,19 +980,25 @@ class Dompdf
|
|
1020 |
/**
|
1021 |
* Gets the paper size
|
1022 |
*
|
1023 |
-
* @
|
1024 |
-
* @return int[] A four-element integer array
|
1025 |
*/
|
1026 |
-
public function getPaperSize(
|
1027 |
{
|
1028 |
-
$
|
1029 |
-
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
} else {
|
1034 |
-
|
|
|
|
|
|
|
|
|
|
|
1035 |
}
|
|
|
|
|
1036 |
}
|
1037 |
|
1038 |
/**
|
@@ -1265,6 +1231,11 @@ class Dompdf
|
|
1265 |
}
|
1266 |
|
1267 |
/**
|
|
|
|
|
|
|
|
|
|
|
1268 |
* @param Canvas $canvas
|
1269 |
* @return $this
|
1270 |
*/
|
@@ -1361,7 +1332,7 @@ class Dompdf
|
|
1361 |
}
|
1362 |
|
1363 |
$this->options = $options;
|
1364 |
-
$fontMetrics = $this->
|
1365 |
if (isset($fontMetrics)) {
|
1366 |
$fontMetrics->setOptions($options);
|
1367 |
}
|
@@ -1397,15 +1368,16 @@ class Dompdf
|
|
1397 |
|
1398 |
/**
|
1399 |
* @param array $callbacks the set of callbacks to set
|
|
|
1400 |
* @deprecated
|
1401 |
*/
|
1402 |
public function set_callbacks($callbacks)
|
1403 |
{
|
1404 |
-
$this->setCallbacks($callbacks);
|
1405 |
}
|
1406 |
|
1407 |
/**
|
1408 |
-
*
|
1409 |
*
|
1410 |
* The callbacks array should contain arrays with `event` set to a callback
|
1411 |
* event name and `f` set to a function or any other callable.
|
@@ -1416,27 +1388,31 @@ class Dompdf
|
|
1416 |
* * `end_frame`: called after frame rendering is complete
|
1417 |
* * `begin_page_render`: called before a page is rendered
|
1418 |
* * `end_page_render`: called after page rendering is complete
|
|
|
1419 |
*
|
1420 |
-
* The function `f`
|
1421 |
-
*
|
1422 |
-
*
|
|
|
1423 |
*
|
1424 |
-
* @param array $callbacks The set of callbacks to set
|
|
|
1425 |
*/
|
1426 |
-
public function setCallbacks($callbacks)
|
1427 |
{
|
1428 |
-
|
1429 |
-
|
1430 |
-
|
1431 |
-
|
1432 |
-
|
1433 |
-
|
1434 |
-
|
1435 |
-
|
1436 |
-
}
|
1437 |
}
|
1438 |
}
|
1439 |
}
|
|
|
|
|
1440 |
}
|
1441 |
|
1442 |
/**
|
14 |
use DOMXPath;
|
15 |
use Dompdf\Frame\Factory;
|
16 |
use Dompdf\Frame\FrameTree;
|
|
|
|
|
17 |
use Dompdf\Image\Cache;
|
18 |
use Dompdf\Css\Stylesheet;
|
19 |
use Dompdf\Helpers;
|
20 |
+
use Masterminds\HTML5;
|
21 |
|
22 |
/**
|
23 |
* Dompdf - PHP5 HTML to PDF renderer
|
196 |
*/
|
197 |
private $quirksmode = false;
|
198 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
/**
|
200 |
* Local file extension whitelist
|
201 |
*
|
248 |
/**
|
249 |
* Class constructor
|
250 |
*
|
251 |
+
* @param Options|array|null $options
|
252 |
*/
|
253 |
public function __construct($options = null)
|
254 |
{
|
261 |
}
|
262 |
|
263 |
$versionFile = realpath(__DIR__ . '/../VERSION');
|
264 |
+
if (($version = file_get_contents($versionFile)) !== false) {
|
265 |
+
$version = trim($version);
|
266 |
+
if ($version !== '$Format:<%h>$') {
|
267 |
+
$this->version = sprintf('dompdf %s', $version);
|
268 |
+
}
|
269 |
}
|
270 |
|
271 |
$this->setPhpConfig();
|
273 |
$this->paperSize = $this->options->getDefaultPaperSize();
|
274 |
$this->paperOrientation = $this->options->getDefaultPaperOrientation();
|
275 |
|
276 |
+
$this->canvas = CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation);
|
277 |
+
$this->fontMetrics = new FontMetrics($this->canvas, $this->options);
|
278 |
$this->css = new Stylesheet($this);
|
279 |
|
280 |
$this->restorePhpConfig();
|
345 |
[$this->protocol, $this->baseHost, $this->basePath] = Helpers::explode_url($file);
|
346 |
}
|
347 |
$protocol = strtolower($this->protocol);
|
|
|
348 |
$uri = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $file);
|
349 |
|
350 |
+
$allowed_protocols = $this->options->getAllowedProtocols();
|
351 |
+
if (!array_key_exists($protocol, $allowed_protocols)) {
|
352 |
throw new Exception("Permission denied on $file. The communication protocol is not supported.");
|
353 |
}
|
354 |
|
355 |
+
if ($protocol === "file://") {
|
356 |
+
$ext = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
357 |
if (!in_array($ext, $this->allowedLocalFileExtensions)) {
|
358 |
+
throw new Exception("Permission denied on $file: The file extension is forbidden.");
|
359 |
}
|
360 |
+
}
|
361 |
|
362 |
+
foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
|
363 |
+
[$result, $message] = $rule($uri);
|
364 |
+
if (!$result) {
|
365 |
+
throw new Exception("Error loading $file: $message");
|
366 |
}
|
|
|
|
|
367 |
}
|
368 |
|
369 |
[$contents, $http_response_header] = Helpers::getFileContent($uri, $this->options->getHttpContext());
|
472 |
|
473 |
try {
|
474 |
// @todo Take the quirksmode into account
|
475 |
+
// https://quirks.spec.whatwg.org/
|
476 |
// http://hsivonen.iki.fi/doctype/
|
|
|
477 |
$quirksmode = false;
|
478 |
|
479 |
+
$html5 = new HTML5(['encoding' => $encoding, 'disable_html_ns' => true]);
|
480 |
+
$dom = $html5->loadHTML($str);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
481 |
|
482 |
+
// extra step to normalize the HTML document structure
|
483 |
+
// see Masterminds/html5-php#166
|
484 |
+
$doc = new DOMDocument("1.0", $encoding);
|
485 |
+
$doc->preserveWhiteSpace = true;
|
486 |
+
$doc->loadHTML($html5->saveHTML($dom), LIBXML_NOWARNING | LIBXML_NOERROR);
|
|
|
487 |
|
488 |
$this->loadDOM($doc, $quirksmode);
|
489 |
} finally {
|
533 |
$acceptedmedia[] = $this->options->getDefaultMediaType();
|
534 |
|
535 |
// <base href="" />
|
536 |
+
/** @var \DOMElement|null */
|
537 |
+
$baseNode = $this->dom->getElementsByTagName("base")->item(0);
|
538 |
+
$baseHref = $baseNode ? $baseNode->getAttribute("href") : "";
|
539 |
+
if ($baseHref !== "") {
|
540 |
+
[$this->protocol, $this->baseHost, $this->basePath] = Helpers::explode_url($baseHref);
|
541 |
}
|
542 |
|
543 |
// Set the base path of the Stylesheet to that of the file being processed
|
579 |
$url = $tag->getAttribute("href");
|
580 |
$url = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $url);
|
581 |
|
582 |
+
if ($url !== null) {
|
583 |
+
$this->css->load_css_file($url, Stylesheet::ORIG_AUTHOR);
|
584 |
+
}
|
585 |
}
|
586 |
break;
|
587 |
|
679 |
public function render()
|
680 |
{
|
681 |
$this->setPhpConfig();
|
|
|
682 |
|
683 |
+
$logOutputFile = $this->options->getLogOutputFile();
|
684 |
if ($logOutputFile) {
|
685 |
if (!file_exists($logOutputFile) && is_writable(dirname($logOutputFile))) {
|
686 |
touch($logOutputFile);
|
705 |
$pageStyle->inherit($basePageStyle);
|
706 |
}
|
707 |
|
708 |
+
// Set paper size if defined via CSS
|
|
|
|
|
709 |
if (is_array($basePageStyle->size)) {
|
710 |
+
[$width, $height] = $basePageStyle->size;
|
711 |
+
$this->setPaper([0, 0, $width, $height]);
|
712 |
}
|
713 |
|
714 |
+
// Create a new canvas instance if the current one does not match the
|
715 |
+
// desired paper size
|
716 |
+
$canvasWidth = $this->canvas->get_width();
|
717 |
+
$canvasHeight = $this->canvas->get_height();
|
718 |
+
$size = $this->getPaperSize();
|
|
|
|
|
|
|
|
|
719 |
|
720 |
+
if ($canvasWidth !== $size[2] || $canvasHeight !== $size[3]) {
|
721 |
+
$this->canvas = CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation);
|
722 |
+
$this->fontMetrics->setCanvas($this->canvas);
|
723 |
+
}
|
724 |
|
725 |
+
$canvas = $this->canvas;
|
726 |
$root = null;
|
727 |
|
728 |
+
foreach ($this->tree as $frame) {
|
729 |
// Set up the root frame
|
730 |
if (is_null($root)) {
|
731 |
$root = Factory::decorate_root($this->tree->get_root(), $this);
|
769 |
// This is where the magic happens:
|
770 |
$root->reflow();
|
771 |
|
772 |
+
if (isset($this->callbacks["end_document"])) {
|
773 |
+
$fs = $this->callbacks["end_document"];
|
774 |
+
|
775 |
+
foreach ($fs as $f) {
|
776 |
+
$canvas->page_script($f);
|
777 |
+
}
|
778 |
+
}
|
779 |
+
|
780 |
// Clean up cached images
|
781 |
if (!$this->options->getDebugKeepTemp()) {
|
782 |
Cache::clear($this->options->getDebugPng());
|
804 |
$this->restorePhpConfig();
|
805 |
}
|
806 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
807 |
/**
|
808 |
* Writes the output buffer in the log file
|
809 |
*
|
830 |
file_put_contents($logOutputFile, $out);
|
831 |
}
|
832 |
|
833 |
+
/**
|
834 |
+
* Add meta information to the PDF after rendering.
|
835 |
+
*
|
836 |
+
* @deprecated
|
837 |
+
*/
|
838 |
+
public function add_info($label, $value)
|
839 |
+
{
|
840 |
+
$this->addInfo($label, $value);
|
841 |
+
}
|
842 |
+
|
843 |
+
/**
|
844 |
+
* Add meta information to the PDF after rendering.
|
845 |
+
*
|
846 |
+
* @param string $label Label of the value (Creator, Producer, etc.)
|
847 |
+
* @param string $value The text to set
|
848 |
+
*/
|
849 |
+
public function addInfo(string $label, string $value): void
|
850 |
+
{
|
851 |
+
$this->canvas->add_info($label, $value);
|
852 |
+
}
|
853 |
+
|
854 |
/**
|
855 |
* Streams the PDF to the client.
|
856 |
*
|
871 |
{
|
872 |
$this->setPhpConfig();
|
873 |
|
874 |
+
$this->canvas->stream($filename, $options);
|
|
|
|
|
|
|
875 |
|
876 |
$this->restorePhpConfig();
|
877 |
}
|
892 |
{
|
893 |
$this->setPhpConfig();
|
894 |
|
895 |
+
$output = $this->canvas->output($options);
|
|
|
|
|
|
|
|
|
|
|
896 |
|
897 |
$this->restorePhpConfig();
|
898 |
|
966 |
/**
|
967 |
* Sets the paper size & orientation
|
968 |
*
|
969 |
+
* @param string|float[] $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}
|
970 |
* @param string $orientation 'portrait' or 'landscape'
|
971 |
* @return $this
|
972 |
*/
|
980 |
/**
|
981 |
* Gets the paper size
|
982 |
*
|
983 |
+
* @return float[] A four-element float array
|
|
|
984 |
*/
|
985 |
+
public function getPaperSize()
|
986 |
{
|
987 |
+
$paper = $this->paperSize;
|
988 |
+
$orientation = $this->paperOrientation;
|
989 |
+
|
990 |
+
if (is_array($paper)) {
|
991 |
+
$size = array_map("floatval", $paper);
|
992 |
} else {
|
993 |
+
$paper = strtolower($paper);
|
994 |
+
$size = CPDF::$PAPER_SIZES[$paper] ?? CPDF::$PAPER_SIZES["letter"];
|
995 |
+
}
|
996 |
+
|
997 |
+
if (strtolower($orientation) === "landscape") {
|
998 |
+
[$size[2], $size[3]] = [$size[3], $size[2]];
|
999 |
}
|
1000 |
+
|
1001 |
+
return $size;
|
1002 |
}
|
1003 |
|
1004 |
/**
|
1231 |
}
|
1232 |
|
1233 |
/**
|
1234 |
+
* Set a custom `Canvas` instance to render the document to.
|
1235 |
+
*
|
1236 |
+
* Be aware that the instance will be replaced on render if the document
|
1237 |
+
* defines a paper size different from the canvas.
|
1238 |
+
*
|
1239 |
* @param Canvas $canvas
|
1240 |
* @return $this
|
1241 |
*/
|
1332 |
}
|
1333 |
|
1334 |
$this->options = $options;
|
1335 |
+
$fontMetrics = $this->fontMetrics;
|
1336 |
if (isset($fontMetrics)) {
|
1337 |
$fontMetrics->setOptions($options);
|
1338 |
}
|
1368 |
|
1369 |
/**
|
1370 |
* @param array $callbacks the set of callbacks to set
|
1371 |
+
* @return $this
|
1372 |
* @deprecated
|
1373 |
*/
|
1374 |
public function set_callbacks($callbacks)
|
1375 |
{
|
1376 |
+
return $this->setCallbacks($callbacks);
|
1377 |
}
|
1378 |
|
1379 |
/**
|
1380 |
+
* Define callbacks that allow modifying the document during render.
|
1381 |
*
|
1382 |
* The callbacks array should contain arrays with `event` set to a callback
|
1383 |
* event name and `f` set to a function or any other callable.
|
1388 |
* * `end_frame`: called after frame rendering is complete
|
1389 |
* * `begin_page_render`: called before a page is rendered
|
1390 |
* * `end_page_render`: called after page rendering is complete
|
1391 |
+
* * `end_document`: called for every page after rendering is complete
|
1392 |
*
|
1393 |
+
* The function `f` receives three arguments `Frame $frame`, `Canvas $canvas`,
|
1394 |
+
* and `FontMetrics $fontMetrics` for all events but `end_document`. For
|
1395 |
+
* `end_document`, the function receives four arguments `int $pageNumber`,
|
1396 |
+
* `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics` instead.
|
1397 |
*
|
1398 |
+
* @param array $callbacks The set of callbacks to set.
|
1399 |
+
* @return $this
|
1400 |
*/
|
1401 |
+
public function setCallbacks(array $callbacks): self
|
1402 |
{
|
1403 |
+
$this->callbacks = [];
|
1404 |
+
|
1405 |
+
foreach ($callbacks as $c) {
|
1406 |
+
if (is_array($c) && isset($c["event"]) && isset($c["f"])) {
|
1407 |
+
$event = $c["event"];
|
1408 |
+
$f = $c["f"];
|
1409 |
+
if (is_string($event) && is_callable($f)) {
|
1410 |
+
$this->callbacks[$event][] = $f;
|
|
|
1411 |
}
|
1412 |
}
|
1413 |
}
|
1414 |
+
|
1415 |
+
return $this;
|
1416 |
}
|
1417 |
|
1418 |
/**
|
vendor/dompdf/dompdf/src/FontMetrics.php
CHANGED
@@ -25,7 +25,7 @@ use FontLib\Font;
|
|
25 |
class FontMetrics
|
26 |
{
|
27 |
/**
|
28 |
-
* Name of the font
|
29 |
*
|
30 |
* This file must be writable by the webserver process only to update it
|
31 |
* with save_font_families() after adding the .afm file references of a new font family
|
@@ -33,13 +33,8 @@ class FontMetrics
|
|
33 |
* This is typically done only from command line with load_font.php on converting
|
34 |
* ttf fonts to ufm with php-font-lib.
|
35 |
*/
|
36 |
-
const
|
37 |
|
38 |
-
/**
|
39 |
-
* @var Canvas
|
40 |
-
* @deprecated
|
41 |
-
*/
|
42 |
-
protected $pdf;
|
43 |
|
44 |
/**
|
45 |
* Underlying {@link Canvas} object to perform text size calculations
|
@@ -49,13 +44,25 @@ class FontMetrics
|
|
49 |
protected $canvas;
|
50 |
|
51 |
/**
|
52 |
-
* Array of font family names to
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
*
|
54 |
-
*
|
|
|
|
|
|
|
|
|
|
|
55 |
*
|
56 |
* @var array
|
57 |
*/
|
58 |
-
protected $
|
59 |
|
60 |
/**
|
61 |
* @var Options
|
@@ -84,29 +91,14 @@ class FontMetrics
|
|
84 |
* Saves the stored font family cache
|
85 |
*
|
86 |
* The name and location of the cache file are determined by {@link
|
87 |
-
* FontMetrics::
|
88 |
* webserver process.
|
89 |
*
|
90 |
* @see FontMetrics::loadFontFamilies()
|
91 |
*/
|
92 |
public function saveFontFamilies()
|
93 |
{
|
94 |
-
|
95 |
-
$cacheData = sprintf("<?php return function (%s, %s) {%s", '$fontDir', '$rootDir', PHP_EOL);
|
96 |
-
$cacheData .= sprintf("return array (%s", PHP_EOL);
|
97 |
-
foreach ($this->fontLookup as $family => $variants) {
|
98 |
-
$cacheData .= sprintf(" '%s' => array(%s", addslashes($family), PHP_EOL);
|
99 |
-
foreach ($variants as $variant => $path) {
|
100 |
-
$path = sprintf("'%s'", $path);
|
101 |
-
$path = str_replace('\'' . $this->options->getFontDir(), '$fontDir . \'', $path);
|
102 |
-
$path = str_replace('\'' . $this->options->getRootDir(), '$rootDir . \'', $path);
|
103 |
-
$cacheData .= sprintf(" '%s' => %s,%s", $variant, $path, PHP_EOL);
|
104 |
-
}
|
105 |
-
$cacheData .= sprintf(" ),%s", PHP_EOL);
|
106 |
-
}
|
107 |
-
$cacheData .= ");" . PHP_EOL;
|
108 |
-
$cacheData .= "}; ?>";
|
109 |
-
file_put_contents($this->getCacheFile(), $cacheData);
|
110 |
}
|
111 |
|
112 |
/**
|
@@ -124,33 +116,45 @@ class FontMetrics
|
|
124 |
*/
|
125 |
public function loadFontFamilies()
|
126 |
{
|
127 |
-
$
|
128 |
-
$
|
129 |
-
|
130 |
-
// FIXME: temporarily define constants for cache files <= v0.6.2
|
131 |
-
if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
|
132 |
-
if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
|
133 |
-
|
134 |
-
$file = $rootDir . "/lib/fonts/dompdf_font_family_cache.dist.php";
|
135 |
-
$distFontsClosure = require $file;
|
136 |
-
$distFonts = is_array($distFontsClosure) ? $distFontsClosure : $distFontsClosure($rootDir);
|
137 |
-
if (!is_readable($this->getCacheFile())) {
|
138 |
-
$this->fontLookup = $distFonts;
|
139 |
-
return;
|
140 |
-
}
|
141 |
|
142 |
-
|
143 |
-
|
|
|
|
|
|
|
|
|
144 |
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
}
|
150 |
}
|
151 |
-
|
152 |
-
// Merge provided fonts
|
153 |
-
$this->fontLookup += $distFonts;
|
154 |
}
|
155 |
|
156 |
/**
|
@@ -183,7 +187,6 @@ class FontMetrics
|
|
183 |
|
184 |
$styleString = $this->getType("{$style['weight']} {$style['style']}");
|
185 |
|
186 |
-
$fontDir = $this->options->getFontDir();
|
187 |
$remoteHash = md5($remoteFile);
|
188 |
|
189 |
$prefix = $fontname . "_" . $styleString;
|
@@ -199,49 +202,30 @@ class FontMetrics
|
|
199 |
$prefix = preg_replace("[\W]", "_", $prefix);
|
200 |
$prefix = preg_replace("/[^-_\w]+/", "", $prefix);
|
201 |
|
202 |
-
$localFile = $
|
|
|
203 |
|
204 |
-
if (isset($entry[$styleString]) && $
|
205 |
return true;
|
206 |
}
|
207 |
|
208 |
-
$cacheEntry = $localFile;
|
209 |
|
210 |
-
$entry[$styleString] = $
|
211 |
|
212 |
// Download the remote file
|
213 |
[$protocol] = Helpers::explode_url($remoteFile);
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
}
|
218 |
-
if ($protocol === "" || $protocol === "file://") {
|
219 |
-
$realfile = realpath($remoteFile);
|
220 |
-
|
221 |
-
$rootDir = realpath($this->options->getRootDir());
|
222 |
-
if (strpos($realfile, $rootDir) !== 0) {
|
223 |
-
$chroot = $this->options->getChroot();
|
224 |
-
$chrootValid = false;
|
225 |
-
foreach ($chroot as $chrootPath) {
|
226 |
-
$chrootPath = realpath($chrootPath);
|
227 |
-
if ($chrootPath !== false && strpos($realfile, $chrootPath) === 0) {
|
228 |
-
$chrootValid = true;
|
229 |
-
break;
|
230 |
-
}
|
231 |
-
}
|
232 |
-
if ($chrootValid !== true) {
|
233 |
-
Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The file could not be found under the paths specified by Options::chroot.", __FILE__, __LINE__);
|
234 |
-
return false;
|
235 |
-
}
|
236 |
-
}
|
237 |
|
238 |
-
|
239 |
-
|
240 |
-
|
|
|
241 |
}
|
242 |
-
|
243 |
-
$remoteFile = $realfile;
|
244 |
}
|
|
|
245 |
list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);
|
246 |
if ($remoteFileContent === null) {
|
247 |
return false;
|
@@ -257,33 +241,33 @@ class FontMetrics
|
|
257 |
return false;
|
258 |
}
|
259 |
|
260 |
-
switch ($font->getFontType()) {
|
261 |
-
case "TrueType":
|
262 |
-
default:
|
263 |
-
$localFile .= ".ttf";
|
264 |
-
break;
|
265 |
-
}
|
266 |
-
|
267 |
$font->parse();
|
268 |
-
$font->saveAdobeFontMetrics("$
|
269 |
$font->close();
|
270 |
|
271 |
unlink($localTempFile);
|
272 |
|
273 |
-
if ( !file_exists("$
|
274 |
return false;
|
275 |
}
|
276 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
// Save the changes
|
278 |
-
file_put_contents($
|
279 |
|
280 |
-
if ( !file_exists($
|
281 |
-
unlink("$
|
282 |
return false;
|
283 |
}
|
284 |
|
285 |
$this->setFontFamily($fontname, $entry);
|
286 |
-
$this->saveFontFamilies();
|
287 |
|
288 |
return true;
|
289 |
}
|
@@ -306,16 +290,15 @@ class FontMetrics
|
|
306 |
/**
|
307 |
* Calculates text size, in points
|
308 |
*
|
309 |
-
* @param string $text
|
310 |
-
* @param string $font
|
311 |
-
* @param float
|
312 |
-
* @param float
|
313 |
-
* @param float
|
314 |
*
|
315 |
-
* @internal param float $spacing word spacing, if any
|
316 |
* @return float
|
317 |
*/
|
318 |
-
public function getTextWidth($text, $font, $size, $wordSpacing = 0.0, $charSpacing = 0.0)
|
319 |
{
|
320 |
// @todo Make sure this cache is efficient before enabling it
|
321 |
static $cache = [];
|
@@ -359,12 +342,12 @@ class FontMetrics
|
|
359 |
/**
|
360 |
* Calculates font height, in points
|
361 |
*
|
362 |
-
* @param string $font
|
363 |
-
* @param float
|
364 |
*
|
365 |
* @return float
|
366 |
*/
|
367 |
-
public function getFontHeight($font, $size)
|
368 |
{
|
369 |
return $this->canvas->get_font_height($font, $size);
|
370 |
}
|
@@ -372,12 +355,12 @@ class FontMetrics
|
|
372 |
/**
|
373 |
* Calculates font baseline, in points
|
374 |
*
|
375 |
-
* @param string $font
|
376 |
-
* @param float
|
377 |
*
|
378 |
* @return float
|
379 |
*/
|
380 |
-
public function getFontBaseline($font, $size)
|
381 |
{
|
382 |
return $this->canvas->get_font_baseline($font, $size);
|
383 |
}
|
@@ -400,10 +383,10 @@ class FontMetrics
|
|
400 |
* ({@link Options::defaultFont}) is used. The font file returned
|
401 |
* is the absolute pathname to the font file on the system.
|
402 |
*
|
403 |
-
* @param string $familyRaw
|
404 |
-
* @param string
|
405 |
*
|
406 |
-
* @return string
|
407 |
*/
|
408 |
public function getFont($familyRaw, $subtypeRaw = "normal")
|
409 |
{
|
@@ -423,11 +406,12 @@ class FontMetrics
|
|
423 |
|
424 |
$subtype = strtolower($subtypeRaw);
|
425 |
|
|
|
426 |
if ($familyRaw) {
|
427 |
$family = str_replace(["'", '"'], "", strtolower($familyRaw));
|
428 |
|
429 |
-
if (isset($
|
430 |
-
return $cache[$familyRaw][$subtypeRaw] = $
|
431 |
}
|
432 |
|
433 |
return null;
|
@@ -435,15 +419,15 @@ class FontMetrics
|
|
435 |
|
436 |
$fallback_families = [strtolower($this->options->getDefaultFont()), "serif"];
|
437 |
foreach ($fallback_families as $family) {
|
438 |
-
if (isset($
|
439 |
-
return $cache[$familyRaw][$subtypeRaw] = $
|
440 |
}
|
441 |
|
442 |
-
if (!isset($
|
443 |
continue;
|
444 |
}
|
445 |
|
446 |
-
$family = $
|
447 |
|
448 |
foreach ($family as $sub => $font) {
|
449 |
if (strpos($subtype, $sub) !== false) {
|
@@ -486,9 +470,10 @@ class FontMetrics
|
|
486 |
public function getFamily($family)
|
487 |
{
|
488 |
$family = str_replace(["'", '"'], "", mb_strtolower($family));
|
|
|
489 |
|
490 |
-
if (isset($
|
491 |
-
return $
|
492 |
}
|
493 |
|
494 |
return null;
|
@@ -547,7 +532,41 @@ class FontMetrics
|
|
547 |
*/
|
548 |
public function getFontFamilies()
|
549 |
{
|
550 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
551 |
}
|
552 |
|
553 |
/**
|
@@ -566,15 +585,17 @@ class FontMetrics
|
|
566 |
*/
|
567 |
public function setFontFamily($fontname, $entry)
|
568 |
{
|
569 |
-
$this->
|
|
|
|
|
570 |
}
|
571 |
|
572 |
/**
|
573 |
* @return string
|
574 |
*/
|
575 |
-
public function
|
576 |
{
|
577 |
-
return $this->options->getFontDir() . '/' . self::
|
578 |
}
|
579 |
|
580 |
/**
|
@@ -584,6 +605,7 @@ class FontMetrics
|
|
584 |
public function setOptions(Options $options)
|
585 |
{
|
586 |
$this->options = $options;
|
|
|
587 |
return $this;
|
588 |
}
|
589 |
|
@@ -602,8 +624,6 @@ class FontMetrics
|
|
602 |
public function setCanvas(Canvas $canvas)
|
603 |
{
|
604 |
$this->canvas = $canvas;
|
605 |
-
// Still write deprecated pdf for now. It might be used by a parent class.
|
606 |
-
$this->pdf = $canvas;
|
607 |
return $this;
|
608 |
}
|
609 |
|
25 |
class FontMetrics
|
26 |
{
|
27 |
/**
|
28 |
+
* Name of the user font families file
|
29 |
*
|
30 |
* This file must be writable by the webserver process only to update it
|
31 |
* with save_font_families() after adding the .afm file references of a new font family
|
33 |
* This is typically done only from command line with load_font.php on converting
|
34 |
* ttf fonts to ufm with php-font-lib.
|
35 |
*/
|
36 |
+
const USER_FONTS_FILE = "installed-fonts.json";
|
37 |
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
/**
|
40 |
* Underlying {@link Canvas} object to perform text size calculations
|
44 |
protected $canvas;
|
45 |
|
46 |
/**
|
47 |
+
* Array of bundled font family names to variants
|
48 |
+
*
|
49 |
+
* @var array
|
50 |
+
*/
|
51 |
+
protected $bundledFonts = [];
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Array of user defined font family names to variants
|
55 |
*
|
56 |
+
* @var array
|
57 |
+
*/
|
58 |
+
protected $userFonts = [];
|
59 |
+
|
60 |
+
/**
|
61 |
+
* combined list of all font families with absolute paths
|
62 |
*
|
63 |
* @var array
|
64 |
*/
|
65 |
+
protected $fontFamilies;
|
66 |
|
67 |
/**
|
68 |
* @var Options
|
91 |
* Saves the stored font family cache
|
92 |
*
|
93 |
* The name and location of the cache file are determined by {@link
|
94 |
+
* FontMetrics::USER_FONTS_FILE}. This file should be writable by the
|
95 |
* webserver process.
|
96 |
*
|
97 |
* @see FontMetrics::loadFontFamilies()
|
98 |
*/
|
99 |
public function saveFontFamilies()
|
100 |
{
|
101 |
+
file_put_contents($this->getUserFontsFilePath(), json_encode($this->userFonts, JSON_PRETTY_PRINT));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
}
|
103 |
|
104 |
/**
|
116 |
*/
|
117 |
public function loadFontFamilies()
|
118 |
{
|
119 |
+
$file = $this->options->getRootDir() . "/lib/fonts/installed-fonts.dist.json";
|
120 |
+
$this->bundledFonts = json_decode(file_get_contents($file), true);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
+
if (is_readable($this->getUserFontsFilePath())) {
|
123 |
+
$this->userFonts = json_decode(file_get_contents($this->getUserFontsFilePath()), true);
|
124 |
+
} else {
|
125 |
+
$this->loadFontFamiliesLegacy();
|
126 |
+
}
|
127 |
+
}
|
128 |
|
129 |
+
private function loadFontFamiliesLegacy()
|
130 |
+
{
|
131 |
+
$legacyCacheFile = $this->options->getFontDir() . '/dompdf_font_family_cache.php';
|
132 |
+
if (is_readable($legacyCacheFile)) {
|
133 |
+
$fontDir = $this->options->getFontDir();
|
134 |
+
$rootDir = $this->options->getRootDir();
|
135 |
+
|
136 |
+
if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
|
137 |
+
if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
|
138 |
+
|
139 |
+
$cacheDataClosure = require $legacyCacheFile;
|
140 |
+
$cacheData = is_array($cacheDataClosure) ? $cacheDataClosure : $cacheDataClosure($fontDir, $rootDir);
|
141 |
+
if (is_array($cacheData)) {
|
142 |
+
foreach ($cacheData as $family => $variants) {
|
143 |
+
if (!isset($this->bundledFonts[$family]) && is_array($variants)) {
|
144 |
+
foreach ($variants as $variant => $variantPath) {
|
145 |
+
$variantName = basename($variantPath);
|
146 |
+
$variantDir = dirname($variantPath);
|
147 |
+
if ($variantDir == $fontDir) {
|
148 |
+
$this->userFonts[$family][$variant] = $variantName;
|
149 |
+
} else {
|
150 |
+
$this->userFonts[$family][$variant] = $variantPath;
|
151 |
+
}
|
152 |
+
}
|
153 |
+
}
|
154 |
+
}
|
155 |
+
$this->saveFontFamilies();
|
156 |
}
|
157 |
}
|
|
|
|
|
|
|
158 |
}
|
159 |
|
160 |
/**
|
187 |
|
188 |
$styleString = $this->getType("{$style['weight']} {$style['style']}");
|
189 |
|
|
|
190 |
$remoteHash = md5($remoteFile);
|
191 |
|
192 |
$prefix = $fontname . "_" . $styleString;
|
202 |
$prefix = preg_replace("[\W]", "_", $prefix);
|
203 |
$prefix = preg_replace("/[^-_\w]+/", "", $prefix);
|
204 |
|
205 |
+
$localFile = $prefix . "_" . $remoteHash;
|
206 |
+
$localFilePath = $this->getOptions()->getFontDir() . "/" . $localFile;
|
207 |
|
208 |
+
if (isset($entry[$styleString]) && $localFilePath == $entry[$styleString]) {
|
209 |
return true;
|
210 |
}
|
211 |
|
|
|
212 |
|
213 |
+
$entry[$styleString] = $localFile;
|
214 |
|
215 |
// Download the remote file
|
216 |
[$protocol] = Helpers::explode_url($remoteFile);
|
217 |
+
$allowed_protocols = $this->options->getAllowedProtocols();
|
218 |
+
if (!array_key_exists($protocol, $allowed_protocols)) {
|
219 |
+
Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The communication protocol is not supported.", __FILE__, __LINE__);
|
220 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
|
222 |
+
foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
|
223 |
+
[$result, $message] = $rule($remoteFile);
|
224 |
+
if ($result !== true) {
|
225 |
+
Helpers::record_warnings(E_USER_WARNING, "Error loading $remoteFile: $message", __FILE__, __LINE__);
|
226 |
}
|
|
|
|
|
227 |
}
|
228 |
+
|
229 |
list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);
|
230 |
if ($remoteFileContent === null) {
|
231 |
return false;
|
241 |
return false;
|
242 |
}
|
243 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
244 |
$font->parse();
|
245 |
+
$font->saveAdobeFontMetrics("$localFilePath.ufm");
|
246 |
$font->close();
|
247 |
|
248 |
unlink($localTempFile);
|
249 |
|
250 |
+
if ( !file_exists("$localFilePath.ufm") ) {
|
251 |
return false;
|
252 |
}
|
253 |
|
254 |
+
$fontExtension = ".ttf";
|
255 |
+
switch ($font->getFontType()) {
|
256 |
+
case "TrueType":
|
257 |
+
default:
|
258 |
+
$fontExtension = ".ttf";
|
259 |
+
break;
|
260 |
+
}
|
261 |
+
|
262 |
// Save the changes
|
263 |
+
file_put_contents($localFilePath.$fontExtension, $remoteFileContent);
|
264 |
|
265 |
+
if ( !file_exists($localFilePath.$fontExtension) ) {
|
266 |
+
unlink("$localFilePath.ufm");
|
267 |
return false;
|
268 |
}
|
269 |
|
270 |
$this->setFontFamily($fontname, $entry);
|
|
|
271 |
|
272 |
return true;
|
273 |
}
|
290 |
/**
|
291 |
* Calculates text size, in points
|
292 |
*
|
293 |
+
* @param string $text The text to be sized
|
294 |
+
* @param string $font The font file to use
|
295 |
+
* @param float $size The font size, in points
|
296 |
+
* @param float $wordSpacing Word spacing, if any
|
297 |
+
* @param float $charSpacing Char spacing, if any
|
298 |
*
|
|
|
299 |
* @return float
|
300 |
*/
|
301 |
+
public function getTextWidth(string $text, $font, float $size, float $wordSpacing = 0.0, float $charSpacing = 0.0): float
|
302 |
{
|
303 |
// @todo Make sure this cache is efficient before enabling it
|
304 |
static $cache = [];
|
342 |
/**
|
343 |
* Calculates font height, in points
|
344 |
*
|
345 |
+
* @param string $font The font file to use
|
346 |
+
* @param float $size The font size, in points
|
347 |
*
|
348 |
* @return float
|
349 |
*/
|
350 |
+
public function getFontHeight($font, float $size): float
|
351 |
{
|
352 |
return $this->canvas->get_font_height($font, $size);
|
353 |
}
|
355 |
/**
|
356 |
* Calculates font baseline, in points
|
357 |
*
|
358 |
+
* @param string $font The font file to use
|
359 |
+
* @param float $size The font size, in points
|
360 |
*
|
361 |
* @return float
|
362 |
*/
|
363 |
+
public function getFontBaseline($font, float $size): float
|
364 |
{
|
365 |
return $this->canvas->get_font_baseline($font, $size);
|
366 |
}
|
383 |
* ({@link Options::defaultFont}) is used. The font file returned
|
384 |
* is the absolute pathname to the font file on the system.
|
385 |
*
|
386 |
+
* @param string|null $familyRaw
|
387 |
+
* @param string $subtypeRaw
|
388 |
*
|
389 |
+
* @return string|null
|
390 |
*/
|
391 |
public function getFont($familyRaw, $subtypeRaw = "normal")
|
392 |
{
|
406 |
|
407 |
$subtype = strtolower($subtypeRaw);
|
408 |
|
409 |
+
$families = $this->getFontFamilies();
|
410 |
if ($familyRaw) {
|
411 |
$family = str_replace(["'", '"'], "", strtolower($familyRaw));
|
412 |
|
413 |
+
if (isset($families[$family][$subtype])) {
|
414 |
+
return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
|
415 |
}
|
416 |
|
417 |
return null;
|
419 |
|
420 |
$fallback_families = [strtolower($this->options->getDefaultFont()), "serif"];
|
421 |
foreach ($fallback_families as $family) {
|
422 |
+
if (isset($families[$family][$subtype])) {
|
423 |
+
return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
|
424 |
}
|
425 |
|
426 |
+
if (!isset($families[$family])) {
|
427 |
continue;
|
428 |
}
|
429 |
|
430 |
+
$family = $families[$family];
|
431 |
|
432 |
foreach ($family as $sub => $font) {
|
433 |
if (strpos($subtype, $sub) !== false) {
|
470 |
public function getFamily($family)
|
471 |
{
|
472 |
$family = str_replace(["'", '"'], "", mb_strtolower($family));
|
473 |
+
$families = $this->getFontFamilies();
|
474 |
|
475 |
+
if (isset($families[$family])) {
|
476 |
+
return $families[$family];
|
477 |
}
|
478 |
|
479 |
return null;
|
532 |
*/
|
533 |
public function getFontFamilies()
|
534 |
{
|
535 |
+
if (!isset($this->fontFamilies)) {
|
536 |
+
$this->setFontFamilies();
|
537 |
+
}
|
538 |
+
return $this->fontFamilies;
|
539 |
+
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* Convert loaded fonts to font lookup table
|
543 |
+
*
|
544 |
+
* @return array
|
545 |
+
*/
|
546 |
+
public function setFontFamilies()
|
547 |
+
{
|
548 |
+
$fontFamilies = [];
|
549 |
+
if (isset($this->bundledFonts) && is_array($this->bundledFonts)) {
|
550 |
+
foreach ($this->bundledFonts as $family => $variants) {
|
551 |
+
if (!isset($fontFamilies[$family])) {
|
552 |
+
$fontFamilies[$family] = array_map(function ($variant) {
|
553 |
+
return $this->getOptions()->getRootDir() . '/lib/fonts/' . $variant;
|
554 |
+
}, $variants);
|
555 |
+
}
|
556 |
+
}
|
557 |
+
}
|
558 |
+
if (isset($this->userFonts) && is_array($this->userFonts)) {
|
559 |
+
foreach ($this->userFonts as $family => $variants) {
|
560 |
+
$fontFamilies[$family] = array_map(function ($variant) {
|
561 |
+
$variantName = basename($variant);
|
562 |
+
if ($variantName === $variant) {
|
563 |
+
return $this->getOptions()->getFontDir() . '/' . $variant;
|
564 |
+
}
|
565 |
+
return $variant;
|
566 |
+
}, $variants);
|
567 |
+
}
|
568 |
+
}
|
569 |
+
$this->fontFamilies = $fontFamilies;
|
570 |
}
|
571 |
|
572 |
/**
|
585 |
*/
|
586 |
public function setFontFamily($fontname, $entry)
|
587 |
{
|
588 |
+
$this->userFonts[mb_strtolower($fontname)] = $entry;
|
589 |
+
$this->saveFontFamilies();
|
590 |
+
unset($this->fontFamilies);
|
591 |
}
|
592 |
|
593 |
/**
|
594 |
* @return string
|
595 |
*/
|
596 |
+
public function getUserFontsFilePath()
|
597 |
{
|
598 |
+
return $this->options->getFontDir() . '/' . self::USER_FONTS_FILE;
|
599 |
}
|
600 |
|
601 |
/**
|
605 |
public function setOptions(Options $options)
|
606 |
{
|
607 |
$this->options = $options;
|
608 |
+
unset($this->fontFamilies);
|
609 |
return $this;
|
610 |
}
|
611 |
|
624 |
public function setCanvas(Canvas $canvas)
|
625 |
{
|
626 |
$this->canvas = $canvas;
|
|
|
|
|
627 |
return $this;
|
628 |
}
|
629 |
|
vendor/dompdf/dompdf/src/Frame.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
namespace Dompdf;
|
4 |
|
5 |
use Dompdf\Css\Style;
|
6 |
-
use Dompdf\Frame\
|
7 |
|
8 |
/**
|
9 |
* @package dompdf
|
@@ -57,14 +57,6 @@ class Frame
|
|
57 |
*/
|
58 |
protected $_style;
|
59 |
|
60 |
-
/**
|
61 |
-
* This frame's original style. Needed for cases where frames are
|
62 |
-
* split across pages.
|
63 |
-
*
|
64 |
-
* @var Style
|
65 |
-
*/
|
66 |
-
protected $_original_style;
|
67 |
-
|
68 |
/**
|
69 |
* This frame's parent in the document tree.
|
70 |
*
|
@@ -72,13 +64,6 @@ class Frame
|
|
72 |
*/
|
73 |
protected $_parent;
|
74 |
|
75 |
-
/**
|
76 |
-
* This frame's children
|
77 |
-
*
|
78 |
-
* @var Frame[]
|
79 |
-
*/
|
80 |
-
protected $_frame_list;
|
81 |
-
|
82 |
/**
|
83 |
* This frame's first child. All children are handled as a
|
84 |
* doubly-linked list.
|
@@ -133,7 +118,7 @@ class Frame
|
|
133 |
/**
|
134 |
* This frame's decorator
|
135 |
*
|
136 |
-
* @var
|
137 |
*/
|
138 |
protected $_decorator;
|
139 |
|
@@ -161,13 +146,6 @@ class Frame
|
|
161 |
*/
|
162 |
public $_float_next_line = false;
|
163 |
|
164 |
-
/**
|
165 |
-
* Whether the frame is a split-off frame
|
166 |
-
*
|
167 |
-
* @var bool
|
168 |
-
*/
|
169 |
-
public $_splitted;
|
170 |
-
|
171 |
/**
|
172 |
* @var int
|
173 |
*/
|
@@ -188,7 +166,6 @@ class Frame
|
|
188 |
$this->_prev_sibling = $this->_next_sibling = null;
|
189 |
|
190 |
$this->_style = null;
|
191 |
-
$this->_original_style = null;
|
192 |
|
193 |
$this->_containing_block = [
|
194 |
"x" => null,
|
@@ -301,13 +278,8 @@ class Frame
|
|
301 |
$this->_parent->get_node()->removeChild($this->_node);
|
302 |
}
|
303 |
|
304 |
-
$this->_style->dispose();
|
305 |
$this->_style = null;
|
306 |
unset($this->_style);
|
307 |
-
|
308 |
-
$this->_original_style->dispose();
|
309 |
-
$this->_original_style = null;
|
310 |
-
unset($this->_original_style);
|
311 |
}
|
312 |
|
313 |
/**
|
@@ -323,9 +295,7 @@ class Frame
|
|
323 |
$this->_containing_block["w"] = null;
|
324 |
$this->_containing_block["h"] = null;
|
325 |
|
326 |
-
$this->_style
|
327 |
-
unset($this->_style);
|
328 |
-
$this->_style = clone $this->_original_style;
|
329 |
}
|
330 |
|
331 |
/**
|
@@ -353,11 +323,12 @@ class Frame
|
|
353 |
}
|
354 |
|
355 |
/**
|
|
|
356 |
* @return Style
|
357 |
*/
|
358 |
public function get_original_style()
|
359 |
{
|
360 |
-
return $this->
|
361 |
}
|
362 |
|
363 |
/**
|
@@ -369,7 +340,7 @@ class Frame
|
|
369 |
}
|
370 |
|
371 |
/**
|
372 |
-
* @return
|
373 |
*/
|
374 |
public function get_decorator()
|
375 |
{
|
@@ -409,17 +380,11 @@ class Frame
|
|
409 |
}
|
410 |
|
411 |
/**
|
412 |
-
* @return
|
413 |
*/
|
414 |
-
public function get_children()
|
415 |
{
|
416 |
-
|
417 |
-
return $this->_frame_list;
|
418 |
-
}
|
419 |
-
|
420 |
-
$this->_frame_list = new FrameList($this);
|
421 |
-
|
422 |
-
return $this->_frame_list;
|
423 |
}
|
424 |
|
425 |
// Layout property accessors
|
@@ -650,11 +615,11 @@ class Frame
|
|
650 |
}
|
651 |
|
652 |
/**
|
653 |
-
* @param null $opacity
|
654 |
*
|
655 |
* @return float
|
656 |
*/
|
657 |
-
public function get_opacity($opacity = null)
|
658 |
{
|
659 |
if ($opacity !== null) {
|
660 |
$this->set_opacity($opacity);
|
@@ -692,18 +657,14 @@ class Frame
|
|
692 |
/**
|
693 |
* @param Style $style
|
694 |
*/
|
695 |
-
public function set_style(Style $style)
|
696 |
{
|
697 |
-
|
698 |
-
$this->_original_style = clone $style;
|
699 |
-
}
|
700 |
-
|
701 |
-
//$style->set_frame($this);
|
702 |
$this->_style = $style;
|
703 |
}
|
704 |
|
705 |
/**
|
706 |
-
* @param
|
707 |
*/
|
708 |
public function set_decorator(FrameDecorator\AbstractFrameDecorator $decorator)
|
709 |
{
|
@@ -761,12 +722,12 @@ class Frame
|
|
761 |
}
|
762 |
|
763 |
/**
|
764 |
-
* @param $opacity
|
765 |
*/
|
766 |
-
public function set_opacity($opacity)
|
767 |
{
|
768 |
$parent = $this->get_parent();
|
769 |
-
$base_opacity =
|
770 |
$this->_opacity = $base_opacity * $opacity;
|
771 |
}
|
772 |
|
@@ -833,7 +794,7 @@ class Frame
|
|
833 |
*
|
834 |
* @return bool
|
835 |
*/
|
836 |
-
public function is_text_node()
|
837 |
{
|
838 |
if (isset($this->_is_cache["text_node"])) {
|
839 |
return $this->_is_cache["text_node"];
|
@@ -845,21 +806,21 @@ class Frame
|
|
845 |
/**
|
846 |
* @return bool
|
847 |
*/
|
848 |
-
public function
|
849 |
{
|
850 |
-
if (isset($this->_is_cache["
|
851 |
-
return $this->_is_cache["
|
852 |
}
|
853 |
|
854 |
$position = $this->get_style()->position;
|
855 |
|
856 |
-
return $this->_is_cache["
|
857 |
}
|
858 |
|
859 |
/**
|
860 |
* @return bool
|
861 |
*/
|
862 |
-
public function is_absolute()
|
863 |
{
|
864 |
if (isset($this->_is_cache["absolute"])) {
|
865 |
return $this->_is_cache["absolute"];
|
@@ -873,13 +834,13 @@ class Frame
|
|
873 |
*
|
874 |
* @return bool
|
875 |
*/
|
876 |
-
public function is_block()
|
877 |
{
|
878 |
if (isset($this->_is_cache["block"])) {
|
879 |
return $this->_is_cache["block"];
|
880 |
}
|
881 |
|
882 |
-
return $this->_is_cache["block"] = in_array($this->get_style()->display, Style
|
883 |
}
|
884 |
|
885 |
/**
|
@@ -914,23 +875,10 @@ class Frame
|
|
914 |
return $this->_is_cache["inline_level"] = in_array($display, Style::INLINE_LEVEL_TYPES, true);
|
915 |
}
|
916 |
|
917 |
-
/**
|
918 |
-
* @deprecated
|
919 |
-
* @return bool
|
920 |
-
*/
|
921 |
-
public function is_inline_block()
|
922 |
-
{
|
923 |
-
if (isset($this->_is_cache["inline_block"])) {
|
924 |
-
return $this->_is_cache["inline_block"];
|
925 |
-
}
|
926 |
-
|
927 |
-
return $this->_is_cache["inline_block"] = ($this->get_style()->display === "inline-block");
|
928 |
-
}
|
929 |
-
|
930 |
/**
|
931 |
* @return bool
|
932 |
*/
|
933 |
-
public function is_in_flow()
|
934 |
{
|
935 |
if (isset($this->_is_cache["in_flow"])) {
|
936 |
return $this->_is_cache["in_flow"];
|
@@ -942,7 +890,7 @@ class Frame
|
|
942 |
/**
|
943 |
* @return bool
|
944 |
*/
|
945 |
-
public function is_pre()
|
946 |
{
|
947 |
if (isset($this->_is_cache["pre"])) {
|
948 |
return $this->_is_cache["pre"];
|
@@ -956,7 +904,7 @@ class Frame
|
|
956 |
/**
|
957 |
* @return bool
|
958 |
*/
|
959 |
-
public function is_table()
|
960 |
{
|
961 |
if (isset($this->_is_cache["table"])) {
|
962 |
return $this->_is_cache["table"];
|
@@ -964,7 +912,7 @@ class Frame
|
|
964 |
|
965 |
$display = $this->get_style()->display;
|
966 |
|
967 |
-
return $this->_is_cache["table"] = in_array($display, Style
|
968 |
}
|
969 |
|
970 |
|
3 |
namespace Dompdf;
|
4 |
|
5 |
use Dompdf\Css\Style;
|
6 |
+
use Dompdf\Frame\FrameListIterator;
|
7 |
|
8 |
/**
|
9 |
* @package dompdf
|
57 |
*/
|
58 |
protected $_style;
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
/**
|
61 |
* This frame's parent in the document tree.
|
62 |
*
|
64 |
*/
|
65 |
protected $_parent;
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
/**
|
68 |
* This frame's first child. All children are handled as a
|
69 |
* doubly-linked list.
|
118 |
/**
|
119 |
* This frame's decorator
|
120 |
*
|
121 |
+
* @var FrameDecorator\AbstractFrameDecorator
|
122 |
*/
|
123 |
protected $_decorator;
|
124 |
|
146 |
*/
|
147 |
public $_float_next_line = false;
|
148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
/**
|
150 |
* @var int
|
151 |
*/
|
166 |
$this->_prev_sibling = $this->_next_sibling = null;
|
167 |
|
168 |
$this->_style = null;
|
|
|
169 |
|
170 |
$this->_containing_block = [
|
171 |
"x" => null,
|
278 |
$this->_parent->get_node()->removeChild($this->_node);
|
279 |
}
|
280 |
|
|
|
281 |
$this->_style = null;
|
282 |
unset($this->_style);
|
|
|
|
|
|
|
|
|
283 |
}
|
284 |
|
285 |
/**
|
295 |
$this->_containing_block["w"] = null;
|
296 |
$this->_containing_block["h"] = null;
|
297 |
|
298 |
+
$this->_style->reset();
|
|
|
|
|
299 |
}
|
300 |
|
301 |
/**
|
323 |
}
|
324 |
|
325 |
/**
|
326 |
+
* @deprecated
|
327 |
* @return Style
|
328 |
*/
|
329 |
public function get_original_style()
|
330 |
{
|
331 |
+
return $this->_style;
|
332 |
}
|
333 |
|
334 |
/**
|
340 |
}
|
341 |
|
342 |
/**
|
343 |
+
* @return FrameDecorator\AbstractFrameDecorator
|
344 |
*/
|
345 |
public function get_decorator()
|
346 |
{
|
380 |
}
|
381 |
|
382 |
/**
|
383 |
+
* @return FrameListIterator
|
384 |
*/
|
385 |
+
public function get_children(): FrameListIterator
|
386 |
{
|
387 |
+
return new FrameListIterator($this);
|
|
|
|
|
|
|
|
|
|
|
|
|
388 |
}
|
389 |
|
390 |
// Layout property accessors
|
615 |
}
|
616 |
|
617 |
/**
|
618 |
+
* @param float|null $opacity
|
619 |
*
|
620 |
* @return float
|
621 |
*/
|
622 |
+
public function get_opacity(?float $opacity = null): float
|
623 |
{
|
624 |
if ($opacity !== null) {
|
625 |
$this->set_opacity($opacity);
|
657 |
/**
|
658 |
* @param Style $style
|
659 |
*/
|
660 |
+
public function set_style(Style $style): void
|
661 |
{
|
662 |
+
// $style->set_frame($this);
|
|
|
|
|
|
|
|
|
663 |
$this->_style = $style;
|
664 |
}
|
665 |
|
666 |
/**
|
667 |
+
* @param FrameDecorator\AbstractFrameDecorator $decorator
|
668 |
*/
|
669 |
public function set_decorator(FrameDecorator\AbstractFrameDecorator $decorator)
|
670 |
{
|
722 |
}
|
723 |
|
724 |
/**
|
725 |
+
* @param float $opacity
|
726 |
*/
|
727 |
+
public function set_opacity(float $opacity): void
|
728 |
{
|
729 |
$parent = $this->get_parent();
|
730 |
+
$base_opacity = $parent && $parent->_opacity !== null ? $parent->_opacity : 1.0;
|
731 |
$this->_opacity = $base_opacity * $opacity;
|
732 |
}
|
733 |
|
794 |
*
|
795 |
* @return bool
|
796 |
*/
|
797 |
+
public function is_text_node(): bool
|
798 |
{
|
799 |
if (isset($this->_is_cache["text_node"])) {
|
800 |
return $this->_is_cache["text_node"];
|
806 |
/**
|
807 |
* @return bool
|
808 |
*/
|
809 |
+
public function is_positioned(): bool
|
810 |
{
|
811 |
+
if (isset($this->_is_cache["positioned"])) {
|
812 |
+
return $this->_is_cache["positioned"];
|
813 |
}
|
814 |
|
815 |
$position = $this->get_style()->position;
|
816 |
|
817 |
+
return $this->_is_cache["positioned"] = in_array($position, Style::POSITIONED_TYPES, true);
|
818 |
}
|
819 |
|
820 |
/**
|
821 |
* @return bool
|
822 |
*/
|
823 |
+
public function is_absolute(): bool
|
824 |
{
|
825 |
if (isset($this->_is_cache["absolute"])) {
|
826 |
return $this->_is_cache["absolute"];
|
834 |
*
|
835 |
* @return bool
|
836 |
*/
|
837 |
+
public function is_block(): bool
|
838 |
{
|
839 |
if (isset($this->_is_cache["block"])) {
|
840 |
return $this->_is_cache["block"];
|
841 |
}
|
842 |
|
843 |
+
return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::BLOCK_TYPES, true);
|
844 |
}
|
845 |
|
846 |
/**
|
875 |
return $this->_is_cache["inline_level"] = in_array($display, Style::INLINE_LEVEL_TYPES, true);
|
876 |
}
|
877 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
878 |
/**
|
879 |
* @return bool
|
880 |
*/
|
881 |
+
public function is_in_flow(): bool
|
882 |
{
|
883 |
if (isset($this->_is_cache["in_flow"])) {
|
884 |
return $this->_is_cache["in_flow"];
|
890 |
/**
|
891 |
* @return bool
|
892 |
*/
|
893 |
+
public function is_pre(): bool
|
894 |
{
|
895 |
if (isset($this->_is_cache["pre"])) {
|
896 |
return $this->_is_cache["pre"];
|
904 |
/**
|
905 |
* @return bool
|
906 |
*/
|
907 |
+
public function is_table(): bool
|
908 |
{
|
909 |
if (isset($this->_is_cache["table"])) {
|
910 |
return $this->_is_cache["table"];
|
912 |
|
913 |
$display = $this->get_style()->display;
|
914 |
|
915 |
+
return $this->_is_cache["table"] = in_array($display, Style::TABLE_TYPES, true);
|
916 |
}
|
917 |
|
918 |
|
vendor/dompdf/dompdf/src/Frame/Factory.php
CHANGED
@@ -190,7 +190,7 @@ class Factory
|
|
190 |
|
191 |
// Handle nodeName
|
192 |
if ($node->nodeName === "img") {
|
193 |
-
$style->display
|
194 |
$decorator = "Image";
|
195 |
$reflower = "Image";
|
196 |
}
|
@@ -239,7 +239,7 @@ class Factory
|
|
239 |
}
|
240 |
|
241 |
$new_style = $dompdf->getCss()->create_style();
|
242 |
-
$new_style->display
|
243 |
$new_style->inherit($style);
|
244 |
$b_f->set_style($new_style);
|
245 |
|
190 |
|
191 |
// Handle nodeName
|
192 |
if ($node->nodeName === "img") {
|
193 |
+
$style->set_prop("display", "-dompdf-image");
|
194 |
$decorator = "Image";
|
195 |
$reflower = "Image";
|
196 |
}
|
239 |
}
|
240 |
|
241 |
$new_style = $dompdf->getCss()->create_style();
|
242 |
+
$new_style->set_prop("display", "-dompdf-list-bullet");
|
243 |
$new_style->inherit($style);
|
244 |
$b_f->set_style($new_style);
|
245 |
|
vendor/dompdf/dompdf/src/Frame/FrameList.php
DELETED
@@ -1,35 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace Dompdf\Frame;
|
3 |
-
|
4 |
-
use Dompdf\Frame;
|
5 |
-
use IteratorAggregate;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Linked-list IteratorAggregate
|
9 |
-
*
|
10 |
-
* @access private
|
11 |
-
* @package dompdf
|
12 |
-
*/
|
13 |
-
class FrameList implements IteratorAggregate
|
14 |
-
{
|
15 |
-
/**
|
16 |
-
* @var Frame
|
17 |
-
*/
|
18 |
-
protected $_frame;
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @param Frame $frame
|
22 |
-
*/
|
23 |
-
function __construct($frame)
|
24 |
-
{
|
25 |
-
$this->_frame = $frame;
|
26 |
-
}
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @return FrameListIterator
|
30 |
-
*/
|
31 |
-
function getIterator(): FrameListIterator
|
32 |
-
{
|
33 |
-
return new FrameListIterator($this->_frame);
|
34 |
-
}
|
35 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/src/Frame/FrameTree.php
CHANGED
@@ -9,6 +9,7 @@ use DOMXPath;
|
|
9 |
|
10 |
use Dompdf\Exception;
|
11 |
use Dompdf\Frame;
|
|
|
12 |
|
13 |
/**
|
14 |
* @package dompdf
|
@@ -27,7 +28,7 @@ use Dompdf\Frame;
|
|
27 |
*
|
28 |
* @package dompdf
|
29 |
*/
|
30 |
-
class FrameTree
|
31 |
{
|
32 |
/**
|
33 |
* Tags to ignore while parsing the tree
|
@@ -124,11 +125,22 @@ class FrameTree
|
|
124 |
/**
|
125 |
* Returns a post-order iterator for all frames in the tree
|
126 |
*
|
127 |
-
* @
|
|
|
128 |
*/
|
129 |
-
public function get_frames()
|
130 |
{
|
131 |
-
return new
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
}
|
133 |
|
134 |
/**
|
9 |
|
10 |
use Dompdf\Exception;
|
11 |
use Dompdf\Frame;
|
12 |
+
use IteratorAggregate;
|
13 |
|
14 |
/**
|
15 |
* @package dompdf
|
28 |
*
|
29 |
* @package dompdf
|
30 |
*/
|
31 |
+
class FrameTree implements IteratorAggregate
|
32 |
{
|
33 |
/**
|
34 |
* Tags to ignore while parsing the tree
|
125 |
/**
|
126 |
* Returns a post-order iterator for all frames in the tree
|
127 |
*
|
128 |
+
* @deprecated Iterate the tree directly instead
|
129 |
+
* @return FrameTreeIterator
|
130 |
*/
|
131 |
+
public function get_frames(): FrameTreeIterator
|
132 |
{
|
133 |
+
return new FrameTreeIterator($this->_root);
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Returns a post-order iterator for all frames in the tree
|
138 |
+
*
|
139 |
+
* @return FrameTreeIterator
|
140 |
+
*/
|
141 |
+
public function getIterator(): FrameTreeIterator
|
142 |
+
{
|
143 |
+
return new FrameTreeIterator($this->_root);
|
144 |
}
|
145 |
|
146 |
/**
|
vendor/dompdf/dompdf/src/Frame/FrameTreeIterator.php
CHANGED
@@ -70,10 +70,7 @@ class FrameTreeIterator implements Iterator
|
|
70 |
|
71 |
public function next(): void
|
72 |
{
|
73 |
-
$b =
|
74 |
-
|
75 |
-
// Pop last element
|
76 |
-
unset($this->_stack[key($this->_stack)]);
|
77 |
$this->_num++;
|
78 |
|
79 |
// Push all children onto the stack in reverse order
|
70 |
|
71 |
public function next(): void
|
72 |
{
|
73 |
+
$b = array_pop($this->_stack);
|
|
|
|
|
|
|
74 |
$this->_num++;
|
75 |
|
76 |
// Push all children onto the stack in reverse order
|
vendor/dompdf/dompdf/src/Frame/FrameTreeList.php
DELETED
@@ -1,35 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace Dompdf\Frame;
|
3 |
-
|
4 |
-
use IteratorAggregate;
|
5 |
-
use Dompdf\Frame;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Pre-order IteratorAggregate
|
9 |
-
*
|
10 |
-
* @access private
|
11 |
-
* @package dompdf
|
12 |
-
*/
|
13 |
-
class FrameTreeList implements IteratorAggregate
|
14 |
-
{
|
15 |
-
/**
|
16 |
-
* @var Frame
|
17 |
-
*/
|
18 |
-
protected $_root;
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @param Frame $root
|
22 |
-
*/
|
23 |
-
public function __construct(Frame $root)
|
24 |
-
{
|
25 |
-
$this->_root = $root;
|
26 |
-
}
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @return FrameTreeIterator
|
30 |
-
*/
|
31 |
-
public function getIterator(): FrameTreeIterator
|
32 |
-
{
|
33 |
-
return new FrameTreeIterator($this->_root);
|
34 |
-
}
|
35 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
CHANGED
@@ -6,13 +6,14 @@ use DOMElement;
|
|
6 |
use DOMNode;
|
7 |
use Dompdf\Helpers;
|
8 |
use Dompdf\Dompdf;
|
|
|
9 |
use Dompdf\Frame;
|
10 |
-
use Dompdf\Frame\FrameTreeList;
|
11 |
use Dompdf\Frame\Factory;
|
|
|
|
|
12 |
use Dompdf\FrameReflower\AbstractFrameReflower;
|
13 |
use Dompdf\Css\Style;
|
14 |
use Dompdf\Positioner\AbstractPositioner;
|
15 |
-
use Dompdf\Exception;
|
16 |
|
17 |
/**
|
18 |
* @package dompdf
|
@@ -84,7 +85,7 @@ abstract class AbstractFrameDecorator extends Frame
|
|
84 |
*
|
85 |
* @var AbstractFrameDecorator
|
86 |
*/
|
87 |
-
private $
|
88 |
|
89 |
/**
|
90 |
* Cache for the get_parent while loop results
|
@@ -107,6 +108,13 @@ abstract class AbstractFrameDecorator extends Frame
|
|
107 |
*/
|
108 |
public $is_split = false;
|
109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
/**
|
111 |
* Class constructor
|
112 |
*
|
@@ -158,7 +166,10 @@ abstract class AbstractFrameDecorator extends Frame
|
|
158 |
function copy(DOMNode $node)
|
159 |
{
|
160 |
$frame = new Frame($node);
|
161 |
-
$
|
|
|
|
|
|
|
162 |
|
163 |
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
|
164 |
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
|
@@ -177,7 +188,10 @@ abstract class AbstractFrameDecorator extends Frame
|
|
177 |
{
|
178 |
$node = $this->_frame->get_node()->cloneNode();
|
179 |
$frame = new Frame($node);
|
180 |
-
$
|
|
|
|
|
|
|
181 |
|
182 |
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
|
183 |
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
|
@@ -205,8 +219,8 @@ abstract class AbstractFrameDecorator extends Frame
|
|
205 |
{
|
206 |
$style = $this->get_style();
|
207 |
$child_style = $style->get_stylesheet()->create_style();
|
|
|
208 |
$child_style->inherit($style);
|
209 |
-
$child_style->display = $display;
|
210 |
|
211 |
$node = $this->get_node()->ownerDocument->createElement($node_name);
|
212 |
$frame = new Frame($node);
|
@@ -228,7 +242,7 @@ abstract class AbstractFrameDecorator extends Frame
|
|
228 |
// clear parent lookup caches
|
229 |
$this->_cached_parent = null;
|
230 |
$this->_block_parent = null;
|
231 |
-
$this->
|
232 |
|
233 |
// Reset all children
|
234 |
foreach ($this->get_children() as $child) {
|
@@ -295,9 +309,12 @@ abstract class AbstractFrameDecorator extends Frame
|
|
295 |
return $this->_frame->get_style();
|
296 |
}
|
297 |
|
|
|
|
|
|
|
298 |
function get_original_style()
|
299 |
{
|
300 |
-
return $this->_frame->
|
301 |
}
|
302 |
|
303 |
function get_containing_block($i = null)
|
@@ -348,7 +365,7 @@ abstract class AbstractFrameDecorator extends Frame
|
|
348 |
$this->_frame->set_id($id);
|
349 |
}
|
350 |
|
351 |
-
function set_style(Style $style)
|
352 |
{
|
353 |
$this->_frame->set_style($style);
|
354 |
}
|
@@ -539,11 +556,19 @@ abstract class AbstractFrameDecorator extends Frame
|
|
539 |
}
|
540 |
|
541 |
/**
|
542 |
-
* @return
|
543 |
*/
|
544 |
-
function
|
545 |
{
|
546 |
-
return new
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
547 |
}
|
548 |
|
549 |
function set_positioner(AbstractPositioner $posn)
|
@@ -624,16 +649,16 @@ abstract class AbstractFrameDecorator extends Frame
|
|
624 |
/**
|
625 |
* @return AbstractFrameDecorator
|
626 |
*/
|
627 |
-
function
|
628 |
{
|
629 |
// Find our nearest relative positioned parent
|
630 |
-
if (isset($this->
|
631 |
-
return $this->
|
632 |
}
|
633 |
|
634 |
$p = $this->get_parent();
|
635 |
while ($p) {
|
636 |
-
if ($p->
|
637 |
break;
|
638 |
}
|
639 |
|
@@ -644,7 +669,7 @@ abstract class AbstractFrameDecorator extends Frame
|
|
644 |
$p = $this->_root;
|
645 |
}
|
646 |
|
647 |
-
return $this->
|
648 |
}
|
649 |
|
650 |
/**
|
@@ -671,36 +696,36 @@ abstract class AbstractFrameDecorator extends Frame
|
|
671 |
}
|
672 |
|
673 |
$this->revert_counter_increment();
|
|
|
674 |
$node = $this->_frame->get_node();
|
675 |
$split = $this->copy($node->cloneNode());
|
676 |
|
677 |
$style = $this->_frame->get_style();
|
678 |
-
$split_style = $split->
|
679 |
|
680 |
// Truncate the box decoration at the split, except for the body
|
681 |
if ($node->nodeName !== "body") {
|
682 |
-
//
|
683 |
-
$style->margin_bottom = 0;
|
684 |
-
$style->padding_bottom = 0;
|
685 |
-
$style->
|
686 |
-
$style->border_bottom_left_radius = 0;
|
687 |
-
$style->border_bottom_right_radius = 0;
|
688 |
-
|
689 |
-
//
|
690 |
-
$split_style->margin_top = 0;
|
691 |
-
$split_style->padding_top = 0;
|
692 |
-
$split_style->
|
693 |
-
$split_style->border_top_left_radius = 0;
|
694 |
-
$split_style->border_top_right_radius = 0;
|
695 |
$split_style->page_break_before = "auto";
|
696 |
}
|
697 |
|
698 |
-
$split_style->text_indent = 0;
|
699 |
$split_style->counter_reset = "none";
|
700 |
|
701 |
-
$split->set_style(clone $split_style);
|
702 |
$this->is_split = true;
|
703 |
-
$split->
|
704 |
$split->_already_pushed = true;
|
705 |
|
706 |
$this->get_parent()->insert_child_after($split, $this);
|
@@ -719,7 +744,7 @@ abstract class AbstractFrameDecorator extends Frame
|
|
719 |
if (!$forced) {
|
720 |
// Reset top margin in case of an unforced page break
|
721 |
// https://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
|
722 |
-
$child->
|
723 |
}
|
724 |
|
725 |
// Add $child and all following siblings to the new split node
|
@@ -873,9 +898,9 @@ abstract class AbstractFrameDecorator extends Frame
|
|
873 |
/**
|
874 |
* @param float $offset_x
|
875 |
* @param float $offset_y
|
876 |
-
* @param bool
|
877 |
*/
|
878 |
-
final function move($offset_x, $offset_y, $ignore_self = false)
|
879 |
{
|
880 |
$this->_positioner->move($this, $offset_x, $offset_y, $ignore_self);
|
881 |
}
|
6 |
use DOMNode;
|
7 |
use Dompdf\Helpers;
|
8 |
use Dompdf\Dompdf;
|
9 |
+
use Dompdf\Exception;
|
10 |
use Dompdf\Frame;
|
|
|
11 |
use Dompdf\Frame\Factory;
|
12 |
+
use Dompdf\Frame\FrameListIterator;
|
13 |
+
use Dompdf\Frame\FrameTreeIterator;
|
14 |
use Dompdf\FrameReflower\AbstractFrameReflower;
|
15 |
use Dompdf\Css\Style;
|
16 |
use Dompdf\Positioner\AbstractPositioner;
|
|
|
17 |
|
18 |
/**
|
19 |
* @package dompdf
|
85 |
*
|
86 |
* @var AbstractFrameDecorator
|
87 |
*/
|
88 |
+
private $_positioned_parent;
|
89 |
|
90 |
/**
|
91 |
* Cache for the get_parent while loop results
|
108 |
*/
|
109 |
public $is_split = false;
|
110 |
|
111 |
+
/**
|
112 |
+
* Whether the frame is a split-off frame
|
113 |
+
*
|
114 |
+
* @var bool
|
115 |
+
*/
|
116 |
+
public $is_split_off = false;
|
117 |
+
|
118 |
/**
|
119 |
* Class constructor
|
120 |
*
|
166 |
function copy(DOMNode $node)
|
167 |
{
|
168 |
$frame = new Frame($node);
|
169 |
+
$style = clone $this->_frame->get_style();
|
170 |
+
|
171 |
+
$style->reset();
|
172 |
+
$frame->set_style($style);
|
173 |
|
174 |
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
|
175 |
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
|
188 |
{
|
189 |
$node = $this->_frame->get_node()->cloneNode();
|
190 |
$frame = new Frame($node);
|
191 |
+
$style = clone $this->_frame->get_style();
|
192 |
+
|
193 |
+
$style->reset();
|
194 |
+
$frame->set_style($style);
|
195 |
|
196 |
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
|
197 |
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
|
219 |
{
|
220 |
$style = $this->get_style();
|
221 |
$child_style = $style->get_stylesheet()->create_style();
|
222 |
+
$child_style->set_prop("display", $display);
|
223 |
$child_style->inherit($style);
|
|
|
224 |
|
225 |
$node = $this->get_node()->ownerDocument->createElement($node_name);
|
226 |
$frame = new Frame($node);
|
242 |
// clear parent lookup caches
|
243 |
$this->_cached_parent = null;
|
244 |
$this->_block_parent = null;
|
245 |
+
$this->_positioned_parent = null;
|
246 |
|
247 |
// Reset all children
|
248 |
foreach ($this->get_children() as $child) {
|
309 |
return $this->_frame->get_style();
|
310 |
}
|
311 |
|
312 |
+
/**
|
313 |
+
* @deprecated
|
314 |
+
*/
|
315 |
function get_original_style()
|
316 |
{
|
317 |
+
return $this->_frame->get_style();
|
318 |
}
|
319 |
|
320 |
function get_containing_block($i = null)
|
365 |
$this->_frame->set_id($id);
|
366 |
}
|
367 |
|
368 |
+
public function set_style(Style $style): void
|
369 |
{
|
370 |
$this->_frame->set_style($style);
|
371 |
}
|
556 |
}
|
557 |
|
558 |
/**
|
559 |
+
* @return FrameListIterator<AbstractFrameDecorator>
|
560 |
*/
|
561 |
+
public function get_children(): FrameListIterator
|
562 |
{
|
563 |
+
return new FrameListIterator($this);
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* @return FrameTreeIterator<AbstractFrameDecorator>
|
568 |
+
*/
|
569 |
+
function get_subtree(): FrameTreeIterator
|
570 |
+
{
|
571 |
+
return new FrameTreeIterator($this);
|
572 |
}
|
573 |
|
574 |
function set_positioner(AbstractPositioner $posn)
|
649 |
/**
|
650 |
* @return AbstractFrameDecorator
|
651 |
*/
|
652 |
+
function find_positioned_parent()
|
653 |
{
|
654 |
// Find our nearest relative positioned parent
|
655 |
+
if (isset($this->_positioned_parent)) {
|
656 |
+
return $this->_positioned_parent;
|
657 |
}
|
658 |
|
659 |
$p = $this->get_parent();
|
660 |
while ($p) {
|
661 |
+
if ($p->is_positioned()) {
|
662 |
break;
|
663 |
}
|
664 |
|
669 |
$p = $this->_root;
|
670 |
}
|
671 |
|
672 |
+
return $this->_positioned_parent = $p;
|
673 |
}
|
674 |
|
675 |
/**
|
696 |
}
|
697 |
|
698 |
$this->revert_counter_increment();
|
699 |
+
|
700 |
$node = $this->_frame->get_node();
|
701 |
$split = $this->copy($node->cloneNode());
|
702 |
|
703 |
$style = $this->_frame->get_style();
|
704 |
+
$split_style = $split->get_style();
|
705 |
|
706 |
// Truncate the box decoration at the split, except for the body
|
707 |
if ($node->nodeName !== "body") {
|
708 |
+
// Clear bottom decoration of original frame
|
709 |
+
$style->margin_bottom = 0.0;
|
710 |
+
$style->padding_bottom = 0.0;
|
711 |
+
$style->border_bottom_width = 0.0;
|
712 |
+
$style->border_bottom_left_radius = 0.0;
|
713 |
+
$style->border_bottom_right_radius = 0.0;
|
714 |
+
|
715 |
+
// Clear top decoration of split frame
|
716 |
+
$split_style->margin_top = 0.0;
|
717 |
+
$split_style->padding_top = 0.0;
|
718 |
+
$split_style->border_top_width = 0.0;
|
719 |
+
$split_style->border_top_left_radius = 0.0;
|
720 |
+
$split_style->border_top_right_radius = 0.0;
|
721 |
$split_style->page_break_before = "auto";
|
722 |
}
|
723 |
|
724 |
+
$split_style->text_indent = 0.0;
|
725 |
$split_style->counter_reset = "none";
|
726 |
|
|
|
727 |
$this->is_split = true;
|
728 |
+
$split->is_split_off = true;
|
729 |
$split->_already_pushed = true;
|
730 |
|
731 |
$this->get_parent()->insert_child_after($split, $this);
|
744 |
if (!$forced) {
|
745 |
// Reset top margin in case of an unforced page break
|
746 |
// https://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
|
747 |
+
$child->get_style()->margin_top = 0.0;
|
748 |
}
|
749 |
|
750 |
// Add $child and all following siblings to the new split node
|
898 |
/**
|
899 |
* @param float $offset_x
|
900 |
* @param float $offset_y
|
901 |
+
* @param bool $ignore_self
|
902 |
*/
|
903 |
+
final function move(float $offset_x, float $offset_y, bool $ignore_self = false): void
|
904 |
{
|
905 |
$this->_positioner->move($this, $offset_x, $offset_y, $ignore_self);
|
906 |
}
|
vendor/dompdf/dompdf/src/FrameDecorator/Block.php
CHANGED
@@ -114,7 +114,7 @@ class Block extends AbstractFrameDecorator
|
|
114 |
* @param Frame $frame
|
115 |
* @return LineBox|null
|
116 |
*/
|
117 |
-
public function add_frame_to_line(Frame $frame)
|
118 |
{
|
119 |
$current_line = $this->_line_boxes[$this->_cl];
|
120 |
$frame->set_containing_line($current_line);
|
@@ -203,7 +203,7 @@ class Block extends AbstractFrameDecorator
|
|
203 |
/**
|
204 |
* @param float $w
|
205 |
*/
|
206 |
-
function increase_line_width($w)
|
207 |
{
|
208 |
$this->_line_boxes[$this->_cl]->w += $w;
|
209 |
}
|
@@ -212,7 +212,7 @@ class Block extends AbstractFrameDecorator
|
|
212 |
* @param float $val
|
213 |
* @param Frame $frame
|
214 |
*/
|
215 |
-
function maximize_line_height($val, Frame $frame)
|
216 |
{
|
217 |
if ($val > $this->_line_boxes[$this->_cl]->h) {
|
218 |
$this->_line_boxes[$this->_cl]->tallest_frame = $frame;
|
@@ -223,7 +223,7 @@ class Block extends AbstractFrameDecorator
|
|
223 |
/**
|
224 |
* @param bool $br
|
225 |
*/
|
226 |
-
function add_line(bool $br = false)
|
227 |
{
|
228 |
$line = $this->_line_boxes[$this->_cl];
|
229 |
|
114 |
* @param Frame $frame
|
115 |
* @return LineBox|null
|
116 |
*/
|
117 |
+
public function add_frame_to_line(Frame $frame): ?LineBox
|
118 |
{
|
119 |
$current_line = $this->_line_boxes[$this->_cl];
|
120 |
$frame->set_containing_line($current_line);
|
203 |
/**
|
204 |
* @param float $w
|
205 |
*/
|
206 |
+
public function increase_line_width(float $w): void
|
207 |
{
|
208 |
$this->_line_boxes[$this->_cl]->w += $w;
|
209 |
}
|
212 |
* @param float $val
|
213 |
* @param Frame $frame
|
214 |
*/
|
215 |
+
public function maximize_line_height(float $val, Frame $frame): void
|
216 |
{
|
217 |
if ($val > $this->_line_boxes[$this->_cl]->h) {
|
218 |
$this->_line_boxes[$this->_cl]->tallest_frame = $frame;
|
223 |
/**
|
224 |
* @param bool $br
|
225 |
*/
|
226 |
+
public function add_line(bool $br = false): void
|
227 |
{
|
228 |
$line = $this->_line_boxes[$this->_cl];
|
229 |
|
vendor/dompdf/dompdf/src/FrameDecorator/Image.php
CHANGED
@@ -57,15 +57,21 @@ class Image extends AbstractFrameDecorator
|
|
57 |
$dompdf->getProtocol(),
|
58 |
$dompdf->getBaseHost(),
|
59 |
$dompdf->getBasePath(),
|
60 |
-
$dompdf
|
61 |
);
|
62 |
|
63 |
if (Cache::is_broken($this->_image_url) &&
|
64 |
$alt = $frame->get_node()->getAttribute("alt")
|
65 |
) {
|
|
|
66 |
$style = $frame->get_style();
|
67 |
-
$
|
68 |
-
$
|
|
|
|
|
|
|
|
|
|
|
69 |
}
|
70 |
}
|
71 |
|
57 |
$dompdf->getProtocol(),
|
58 |
$dompdf->getBaseHost(),
|
59 |
$dompdf->getBasePath(),
|
60 |
+
$dompdf->getOptions()
|
61 |
);
|
62 |
|
63 |
if (Cache::is_broken($this->_image_url) &&
|
64 |
$alt = $frame->get_node()->getAttribute("alt")
|
65 |
) {
|
66 |
+
$fontMetrics = $dompdf->getFontMetrics();
|
67 |
$style = $frame->get_style();
|
68 |
+
$font = $style->font_family;
|
69 |
+
$size = $style->font_size;
|
70 |
+
$word_spacing = $style->word_spacing;
|
71 |
+
$letter_spacing = $style->letter_spacing;
|
72 |
+
|
73 |
+
$style->width = (4 / 3) * $fontMetrics->getTextWidth($alt, $font, $size, $word_spacing, $letter_spacing);
|
74 |
+
$style->height = $fontMetrics->getFontHeight($font, $size);
|
75 |
}
|
76 |
}
|
77 |
|
vendor/dompdf/dompdf/src/FrameDecorator/Inline.php
CHANGED
@@ -70,18 +70,22 @@ class Inline extends AbstractFrameDecorator
|
|
70 |
$split = $this->copy($node->cloneNode());
|
71 |
|
72 |
$style = $this->_frame->get_style();
|
73 |
-
$split_style = $split->
|
74 |
|
75 |
// Unset the current node's right style properties
|
76 |
-
$style->margin_right = 0;
|
77 |
-
$style->padding_right = 0;
|
78 |
-
$style->border_right_width = 0;
|
|
|
|
|
79 |
|
80 |
// Unset the split node's left style properties since we don't want them
|
81 |
// to propagate
|
82 |
-
$split_style->margin_left = 0;
|
83 |
-
$split_style->padding_left = 0;
|
84 |
-
$split_style->border_left_width = 0;
|
|
|
|
|
85 |
|
86 |
// If this is a generated node don't propagate the content style
|
87 |
if ($split->get_node()->nodeName == "dompdf_generated") {
|
@@ -91,14 +95,12 @@ class Inline extends AbstractFrameDecorator
|
|
91 |
//On continuation of inline element on next line,
|
92 |
//don't repeat non-horizontally repeatable background images
|
93 |
//See e.g. in testcase image_variants, long descriptions
|
94 |
-
if (($url = $
|
95 |
-
&& ($repeat = $
|
96 |
) {
|
97 |
$split_style->background_image = "none";
|
98 |
}
|
99 |
|
100 |
-
$split->set_style(clone $split_style);
|
101 |
-
|
102 |
$this->get_parent()->insert_child_after($split, $this);
|
103 |
|
104 |
// Add $child and all following siblings to the new split node
|
70 |
$split = $this->copy($node->cloneNode());
|
71 |
|
72 |
$style = $this->_frame->get_style();
|
73 |
+
$split_style = $split->get_style();
|
74 |
|
75 |
// Unset the current node's right style properties
|
76 |
+
$style->margin_right = 0.0;
|
77 |
+
$style->padding_right = 0.0;
|
78 |
+
$style->border_right_width = 0.0;
|
79 |
+
$style->border_top_right_radius = 0.0;
|
80 |
+
$style->border_bottom_right_radius = 0.0;
|
81 |
|
82 |
// Unset the split node's left style properties since we don't want them
|
83 |
// to propagate
|
84 |
+
$split_style->margin_left = 0.0;
|
85 |
+
$split_style->padding_left = 0.0;
|
86 |
+
$split_style->border_left_width = 0.0;
|
87 |
+
$split_style->border_top_left_radius = 0.0;
|
88 |
+
$split_style->border_bottom_left_radius = 0.0;
|
89 |
|
90 |
// If this is a generated node don't propagate the content style
|
91 |
if ($split->get_node()->nodeName == "dompdf_generated") {
|
95 |
//On continuation of inline element on next line,
|
96 |
//don't repeat non-horizontally repeatable background images
|
97 |
//See e.g. in testcase image_variants, long descriptions
|
98 |
+
if (($url = $style->background_image) && $url !== "none"
|
99 |
+
&& ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-x"
|
100 |
) {
|
101 |
$split_style->background_image = "none";
|
102 |
}
|
103 |
|
|
|
|
|
104 |
$this->get_parent()->insert_child_after($split, $this);
|
105 |
|
106 |
// Add $child and all following siblings to the new split node
|
vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php
CHANGED
@@ -39,21 +39,6 @@ class ListBullet extends AbstractFrameDecorator
|
|
39 |
*/
|
40 |
public const MARKER_INDENT = 0.52;
|
41 |
|
42 |
-
/**
|
43 |
-
* @deprecated
|
44 |
-
*/
|
45 |
-
const BULLET_PADDING = 1;
|
46 |
-
|
47 |
-
/**
|
48 |
-
* @deprecated
|
49 |
-
*/
|
50 |
-
const BULLET_DESCENT = 0.3;
|
51 |
-
|
52 |
-
/**
|
53 |
-
* @deprecated
|
54 |
-
*/
|
55 |
-
static $BULLET_TYPES = ["disc", "circle", "square"];
|
56 |
-
|
57 |
/**
|
58 |
* ListBullet constructor.
|
59 |
* @param Frame $frame
|
39 |
*/
|
40 |
public const MARKER_INDENT = 0.52;
|
41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
/**
|
43 |
* ListBullet constructor.
|
44 |
* @param Frame $frame
|
vendor/dompdf/dompdf/src/FrameDecorator/Page.php
CHANGED
@@ -178,9 +178,7 @@ class Page extends AbstractFrameDecorator
|
|
178 |
) {
|
179 |
// Prevent cascading splits
|
180 |
$frame->split(null, true, true);
|
181 |
-
|
182 |
-
// $frame->style to the frame's original style.
|
183 |
-
$frame->get_style()->page_break_before = "auto";
|
184 |
$this->_page_full = true;
|
185 |
$frame->_already_pushed = true;
|
186 |
|
@@ -472,7 +470,7 @@ class Page extends AbstractFrameDecorator
|
|
472 |
$prev_group = $frame->get_parent()->get_prev_sibling();
|
473 |
|
474 |
if ($prev_group
|
475 |
-
&& in_array($prev_group->get_style()->display, Table
|
476 |
) {
|
477 |
$prev = $prev_group->get_last_child();
|
478 |
}
|
@@ -511,7 +509,7 @@ class Page extends AbstractFrameDecorator
|
|
511 |
|
512 |
return true;
|
513 |
} else {
|
514 |
-
if (in_array($display, Table
|
515 |
|
516 |
// Disallow breaks at row-groups: only split at row boundaries
|
517 |
return false;
|
@@ -640,7 +638,7 @@ class Page extends AbstractFrameDecorator
|
|
640 |
|
641 |
if ($next->is_table() && !$iter->is_table()) {
|
642 |
$this->_in_table++;
|
643 |
-
}
|
644 |
$this->_in_table--;
|
645 |
}
|
646 |
|
178 |
) {
|
179 |
// Prevent cascading splits
|
180 |
$frame->split(null, true, true);
|
181 |
+
$style->page_break_before = "auto";
|
|
|
|
|
182 |
$this->_page_full = true;
|
183 |
$frame->_already_pushed = true;
|
184 |
|
470 |
$prev_group = $frame->get_parent()->get_prev_sibling();
|
471 |
|
472 |
if ($prev_group
|
473 |
+
&& in_array($prev_group->get_style()->display, Table::ROW_GROUPS, true)
|
474 |
) {
|
475 |
$prev = $prev_group->get_last_child();
|
476 |
}
|
509 |
|
510 |
return true;
|
511 |
} else {
|
512 |
+
if (in_array($display, Table::ROW_GROUPS, true)) {
|
513 |
|
514 |
// Disallow breaks at row-groups: only split at row boundaries
|
515 |
return false;
|
638 |
|
639 |
if ($next->is_table() && !$iter->is_table()) {
|
640 |
$this->_in_table++;
|
641 |
+
} elseif (!$next->is_table() && $iter->is_table()) {
|
642 |
$this->_in_table--;
|
643 |
}
|
644 |
|
vendor/dompdf/dompdf/src/FrameDecorator/Table.php
CHANGED
@@ -20,9 +20,12 @@ use Dompdf\Frame;
|
|
20 |
*/
|
21 |
class Table extends AbstractFrameDecorator
|
22 |
{
|
23 |
-
public
|
24 |
|
25 |
-
|
|
|
|
|
|
|
26 |
"table-row-group",
|
27 |
"table-header-group",
|
28 |
"table-footer-group"
|
@@ -36,20 +39,6 @@ class Table extends AbstractFrameDecorator
|
|
36 |
*/
|
37 |
protected $_cellmap;
|
38 |
|
39 |
-
/**
|
40 |
-
* The minimum width of the table, in pt
|
41 |
-
*
|
42 |
-
* @var float
|
43 |
-
*/
|
44 |
-
protected $_min_width;
|
45 |
-
|
46 |
-
/**
|
47 |
-
* The maximum width of the table, in pt
|
48 |
-
*
|
49 |
-
* @var float
|
50 |
-
*/
|
51 |
-
protected $_max_width;
|
52 |
-
|
53 |
/**
|
54 |
* Table header rows. Each table header is duplicated when a table
|
55 |
* spans pages.
|
@@ -81,8 +70,6 @@ class Table extends AbstractFrameDecorator
|
|
81 |
$this->_cellmap->set_layout_fixed(true);
|
82 |
}
|
83 |
|
84 |
-
$this->_min_width = null;
|
85 |
-
$this->_max_width = null;
|
86 |
$this->_headers = [];
|
87 |
$this->_footers = [];
|
88 |
}
|
@@ -91,8 +78,6 @@ class Table extends AbstractFrameDecorator
|
|
91 |
{
|
92 |
parent::reset();
|
93 |
$this->_cellmap->reset();
|
94 |
-
$this->_min_width = null;
|
95 |
-
$this->_max_width = null;
|
96 |
$this->_headers = [];
|
97 |
$this->_footers = [];
|
98 |
$this->_reflower->reset();
|
@@ -134,7 +119,7 @@ class Table extends AbstractFrameDecorator
|
|
134 |
|
135 |
parent::split($first_header, $page_break, $forced);
|
136 |
|
137 |
-
} elseif (in_array($child->get_style()->display, self
|
138 |
|
139 |
// Individual rows should have already been handled
|
140 |
parent::split($child, $page_break, $forced);
|
@@ -191,46 +176,6 @@ class Table extends AbstractFrameDecorator
|
|
191 |
return $this->_cellmap;
|
192 |
}
|
193 |
|
194 |
-
/**
|
195 |
-
* Return the minimum width of this table
|
196 |
-
*
|
197 |
-
* @return float
|
198 |
-
*/
|
199 |
-
public function get_min_width()
|
200 |
-
{
|
201 |
-
return $this->_min_width;
|
202 |
-
}
|
203 |
-
|
204 |
-
/**
|
205 |
-
* Return the maximum width of this table
|
206 |
-
*
|
207 |
-
* @return float
|
208 |
-
*/
|
209 |
-
public function get_max_width()
|
210 |
-
{
|
211 |
-
return $this->_max_width;
|
212 |
-
}
|
213 |
-
|
214 |
-
/**
|
215 |
-
* Set the minimum width of the table
|
216 |
-
*
|
217 |
-
* @param float $width the new minimum width
|
218 |
-
*/
|
219 |
-
public function set_min_width($width)
|
220 |
-
{
|
221 |
-
$this->_min_width = $width;
|
222 |
-
}
|
223 |
-
|
224 |
-
/**
|
225 |
-
* Set the maximum width of the table
|
226 |
-
*
|
227 |
-
* @param float $width the new maximum width
|
228 |
-
*/
|
229 |
-
public function set_max_width($width)
|
230 |
-
{
|
231 |
-
$this->_max_width = $width;
|
232 |
-
}
|
233 |
-
|
234 |
//........................................................................
|
235 |
|
236 |
/**
|
@@ -248,7 +193,7 @@ class Table extends AbstractFrameDecorator
|
|
248 |
$wsPattern = '/^[^\S\xA0\x{202F}\x{2007}]*$/u';
|
249 |
$validChildOrNull = function ($frame) {
|
250 |
return $frame === null
|
251 |
-
|| in_array($frame->get_style()->display, self
|
252 |
};
|
253 |
|
254 |
return $frame->is_text_node() && !$frame->is_pre()
|
@@ -273,7 +218,7 @@ class Table extends AbstractFrameDecorator
|
|
273 |
foreach ($children as $child) {
|
274 |
$display = $child->get_style()->display;
|
275 |
|
276 |
-
if (in_array($display, self
|
277 |
// Reset anonymous tbody
|
278 |
$tbody = null;
|
279 |
|
@@ -314,7 +259,7 @@ class Table extends AbstractFrameDecorator
|
|
314 |
foreach ($this->get_children() as $child) {
|
315 |
$display = $child->get_style()->display;
|
316 |
|
317 |
-
if (in_array($display, self
|
318 |
$this->normalizeRowGroup($child);
|
319 |
}
|
320 |
}
|
@@ -395,26 +340,4 @@ class Table extends AbstractFrameDecorator
|
|
395 |
$frame->append_child($td);
|
396 |
}
|
397 |
}
|
398 |
-
|
399 |
-
//........................................................................
|
400 |
-
|
401 |
-
/**
|
402 |
-
* @deprecated
|
403 |
-
*/
|
404 |
-
public function normalise()
|
405 |
-
{
|
406 |
-
$this->normalize();
|
407 |
-
}
|
408 |
-
|
409 |
-
/**
|
410 |
-
* Moves the specified frame and it's corresponding node outside of
|
411 |
-
* the table.
|
412 |
-
*
|
413 |
-
* @deprecated
|
414 |
-
* @param Frame $frame the frame to move
|
415 |
-
*/
|
416 |
-
public function move_after(Frame $frame)
|
417 |
-
{
|
418 |
-
$this->get_parent()->insert_child_after($frame, $this);
|
419 |
-
}
|
420 |
}
|
20 |
*/
|
21 |
class Table extends AbstractFrameDecorator
|
22 |
{
|
23 |
+
public const VALID_CHILDREN = Style::TABLE_INTERNAL_TYPES;
|
24 |
|
25 |
+
/**
|
26 |
+
* List of all row-group display types.
|
27 |
+
*/
|
28 |
+
public const ROW_GROUPS = [
|
29 |
"table-row-group",
|
30 |
"table-header-group",
|
31 |
"table-footer-group"
|
39 |
*/
|
40 |
protected $_cellmap;
|
41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
/**
|
43 |
* Table header rows. Each table header is duplicated when a table
|
44 |
* spans pages.
|
70 |
$this->_cellmap->set_layout_fixed(true);
|
71 |
}
|
72 |
|
|
|
|
|
73 |
$this->_headers = [];
|
74 |
$this->_footers = [];
|
75 |
}
|
78 |
{
|
79 |
parent::reset();
|
80 |
$this->_cellmap->reset();
|
|
|
|
|
81 |
$this->_headers = [];
|
82 |
$this->_footers = [];
|
83 |
$this->_reflower->reset();
|
119 |
|
120 |
parent::split($first_header, $page_break, $forced);
|
121 |
|
122 |
+
} elseif (in_array($child->get_style()->display, self::ROW_GROUPS, true)) {
|
123 |
|
124 |
// Individual rows should have already been handled
|
125 |
parent::split($child, $page_break, $forced);
|
176 |
return $this->_cellmap;
|
177 |
}
|
178 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
//........................................................................
|
180 |
|
181 |
/**
|
193 |
$wsPattern = '/^[^\S\xA0\x{202F}\x{2007}]*$/u';
|
194 |
$validChildOrNull = function ($frame) {
|
195 |
return $frame === null
|
196 |
+
|| in_array($frame->get_style()->display, self::VALID_CHILDREN, true);
|
197 |
};
|
198 |
|
199 |
return $frame->is_text_node() && !$frame->is_pre()
|
218 |
foreach ($children as $child) {
|
219 |
$display = $child->get_style()->display;
|
220 |
|
221 |
+
if (in_array($display, self::ROW_GROUPS, true)) {
|
222 |
// Reset anonymous tbody
|
223 |
$tbody = null;
|
224 |
|
259 |
foreach ($this->get_children() as $child) {
|
260 |
$display = $child->get_style()->display;
|
261 |
|
262 |
+
if (in_array($display, self::ROW_GROUPS, true)) {
|
263 |
$this->normalizeRowGroup($child);
|
264 |
}
|
265 |
}
|
340 |
$frame->append_child($td);
|
341 |
}
|
342 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
343 |
}
|
vendor/dompdf/dompdf/src/FrameDecorator/TableCell.php
CHANGED
@@ -81,7 +81,7 @@ class TableCell extends BlockFrameDecorator
|
|
81 |
);
|
82 |
|
83 |
$new_height = $height - $v_space;
|
84 |
-
$style->height
|
85 |
|
86 |
if ($new_height > $this->_content_height) {
|
87 |
$y_offset = 0;
|
81 |
);
|
82 |
|
83 |
$new_height = $height - $v_space;
|
84 |
+
$style->set_used("height", $new_height);
|
85 |
|
86 |
if ($new_height > $this->_content_height) {
|
87 |
$y_offset = 0;
|
vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php
CHANGED
@@ -9,7 +9,6 @@ namespace Dompdf\FrameDecorator;
|
|
9 |
|
10 |
use Dompdf\Dompdf;
|
11 |
use Dompdf\Frame;
|
12 |
-
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
|
13 |
|
14 |
/**
|
15 |
* Decorates Frames for table row layout
|
@@ -27,32 +26,4 @@ class TableRow extends AbstractFrameDecorator
|
|
27 |
{
|
28 |
parent::__construct($frame, $dompdf);
|
29 |
}
|
30 |
-
|
31 |
-
//........................................................................
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Remove all non table-cell frames from this row and move them after
|
35 |
-
* the table.
|
36 |
-
*
|
37 |
-
* @deprecated
|
38 |
-
*/
|
39 |
-
function normalise()
|
40 |
-
{
|
41 |
-
// Find our table parent
|
42 |
-
$p = TableFrameDecorator::find_parent_table($this);
|
43 |
-
|
44 |
-
$erroneous_frames = [];
|
45 |
-
foreach ($this->get_children() as $child) {
|
46 |
-
$display = $child->get_style()->display;
|
47 |
-
|
48 |
-
if ($display !== "table-cell") {
|
49 |
-
$erroneous_frames[] = $child;
|
50 |
-
}
|
51 |
-
}
|
52 |
-
|
53 |
-
// dump the extra nodes after the table.
|
54 |
-
foreach ($erroneous_frames as $frame) {
|
55 |
-
$p->move_after($frame);
|
56 |
-
}
|
57 |
-
}
|
58 |
}
|
9 |
|
10 |
use Dompdf\Dompdf;
|
11 |
use Dompdf\Frame;
|
|
|
12 |
|
13 |
/**
|
14 |
* Decorates Frames for table row layout
|
26 |
{
|
27 |
parent::__construct($frame, $dompdf);
|
28 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
}
|
vendor/dompdf/dompdf/src/FrameDecorator/Text.php
CHANGED
@@ -22,9 +22,9 @@ use Dompdf\Exception;
|
|
22 |
class Text extends AbstractFrameDecorator
|
23 |
{
|
24 |
/**
|
25 |
-
* @var float
|
26 |
*/
|
27 |
-
protected $
|
28 |
|
29 |
/**
|
30 |
* Text constructor.
|
@@ -39,23 +39,23 @@ class Text extends AbstractFrameDecorator
|
|
39 |
}
|
40 |
|
41 |
parent::__construct($frame, $dompdf);
|
42 |
-
$this->
|
43 |
}
|
44 |
|
45 |
function reset()
|
46 |
{
|
47 |
parent::reset();
|
48 |
-
$this->
|
49 |
}
|
50 |
|
51 |
// Accessor methods
|
52 |
|
53 |
/**
|
54 |
-
* @return float
|
55 |
*/
|
56 |
-
function get_text_spacing()
|
57 |
{
|
58 |
-
return $this->
|
59 |
}
|
60 |
|
61 |
/**
|
@@ -118,15 +118,10 @@ class Text extends AbstractFrameDecorator
|
|
118 |
/**
|
119 |
* @param float $spacing
|
120 |
*/
|
121 |
-
function set_text_spacing($spacing)
|
122 |
{
|
123 |
-
$
|
124 |
-
|
125 |
-
$this->_text_spacing = $spacing;
|
126 |
-
$char_spacing = (float)$style->length_in_pt($style->letter_spacing);
|
127 |
-
|
128 |
-
// Re-adjust our width to account for the change in spacing
|
129 |
-
$style->width = $this->_dompdf->getFontMetrics()->getTextWidth($this->get_text(), $style->font_family, $style->font_size, $spacing, $char_spacing);
|
130 |
}
|
131 |
|
132 |
/**
|
@@ -134,16 +129,19 @@ class Text extends AbstractFrameDecorator
|
|
134 |
*
|
135 |
* @return float
|
136 |
*/
|
137 |
-
function recalculate_width()
|
138 |
{
|
|
|
139 |
$style = $this->get_style();
|
140 |
$text = $this->get_text();
|
141 |
-
$size = $style->font_size;
|
142 |
$font = $style->font_family;
|
143 |
-
$
|
144 |
-
$
|
|
|
|
|
145 |
|
146 |
-
|
|
|
147 |
}
|
148 |
|
149 |
// Text manipulation methods
|
22 |
class Text extends AbstractFrameDecorator
|
23 |
{
|
24 |
/**
|
25 |
+
* @var float
|
26 |
*/
|
27 |
+
protected $text_spacing;
|
28 |
|
29 |
/**
|
30 |
* Text constructor.
|
39 |
}
|
40 |
|
41 |
parent::__construct($frame, $dompdf);
|
42 |
+
$this->text_spacing = 0.0;
|
43 |
}
|
44 |
|
45 |
function reset()
|
46 |
{
|
47 |
parent::reset();
|
48 |
+
$this->text_spacing = 0.0;
|
49 |
}
|
50 |
|
51 |
// Accessor methods
|
52 |
|
53 |
/**
|
54 |
+
* @return float
|
55 |
*/
|
56 |
+
public function get_text_spacing(): float
|
57 |
{
|
58 |
+
return $this->text_spacing;
|
59 |
}
|
60 |
|
61 |
/**
|
118 |
/**
|
119 |
* @param float $spacing
|
120 |
*/
|
121 |
+
public function set_text_spacing(float $spacing): void
|
122 |
{
|
123 |
+
$this->text_spacing = $spacing;
|
124 |
+
$this->recalculate_width();
|
|
|
|
|
|
|
|
|
|
|
125 |
}
|
126 |
|
127 |
/**
|
129 |
*
|
130 |
* @return float
|
131 |
*/
|
132 |
+
public function recalculate_width(): float
|
133 |
{
|
134 |
+
$fontMetrics = $this->_dompdf->getFontMetrics();
|
135 |
$style = $this->get_style();
|
136 |
$text = $this->get_text();
|
|
|
137 |
$font = $style->font_family;
|
138 |
+
$size = $style->font_size;
|
139 |
+
$word_spacing = $this->text_spacing + $style->word_spacing;
|
140 |
+
$letter_spacing = $style->letter_spacing;
|
141 |
+
$text_width = $fontMetrics->getTextWidth($text, $font, $size, $word_spacing, $letter_spacing);
|
142 |
|
143 |
+
$style->set_used("width", $text_width);
|
144 |
+
return $text_width;
|
145 |
}
|
146 |
|
147 |
// Text manipulation methods
|
vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php
CHANGED
@@ -57,10 +57,6 @@ abstract class AbstractFrameReflower
|
|
57 |
$this->_min_max_cache = null;
|
58 |
}
|
59 |
|
60 |
-
function dispose()
|
61 |
-
{
|
62 |
-
}
|
63 |
-
|
64 |
/**
|
65 |
* @return Dompdf
|
66 |
*/
|
@@ -87,7 +83,7 @@ abstract class AbstractFrameReflower
|
|
87 |
|
88 |
switch ($style->position) {
|
89 |
case "absolute":
|
90 |
-
$parent = $frame->
|
91 |
if ($parent !== $frame->get_root()) {
|
92 |
$parent_style = $parent->get_style();
|
93 |
$parent_padding_box = $parent->get_padding_box();
|
@@ -124,7 +120,7 @@ abstract class AbstractFrameReflower
|
|
124 |
* Collapse frames margins
|
125 |
* http://www.w3.org/TR/CSS21/box.html#collapsing-margins
|
126 |
*/
|
127 |
-
protected function _collapse_margins()
|
128 |
{
|
129 |
$frame = $this->_frame;
|
130 |
|
@@ -143,13 +139,13 @@ abstract class AbstractFrameReflower
|
|
143 |
|
144 |
// Handle 'auto' values
|
145 |
if ($t === "auto") {
|
146 |
-
$style->margin_top
|
147 |
-
$t = 0;
|
148 |
}
|
149 |
|
150 |
if ($b === "auto") {
|
151 |
-
$style->margin_bottom
|
152 |
-
$b = 0;
|
153 |
}
|
154 |
|
155 |
// Collapse vertical margins:
|
@@ -171,9 +167,9 @@ abstract class AbstractFrameReflower
|
|
171 |
$n_style = $n->get_style();
|
172 |
$n_t = (float)$n_style->length_in_pt($n_style->margin_top, $cb["w"]);
|
173 |
|
174 |
-
$b = $this->
|
175 |
-
$style->margin_bottom
|
176 |
-
$n_style->margin_top
|
177 |
}
|
178 |
|
179 |
// Collapse our first child's margin, if there is no border or padding
|
@@ -197,9 +193,9 @@ abstract class AbstractFrameReflower
|
|
197 |
$f_style = $f->get_style();
|
198 |
$f_t = (float)$f_style->length_in_pt($f_style->margin_top, $cb["w"]);
|
199 |
|
200 |
-
$t = $this->
|
201 |
-
$style->margin_top
|
202 |
-
$f_style->margin_top
|
203 |
}
|
204 |
}
|
205 |
|
@@ -224,9 +220,9 @@ abstract class AbstractFrameReflower
|
|
224 |
$l_style = $l->get_style();
|
225 |
$l_b = (float)$l_style->length_in_pt($l_style->margin_bottom, $cb["w"]);
|
226 |
|
227 |
-
$b = $this->
|
228 |
-
$style->margin_bottom
|
229 |
-
$l_style->margin_bottom
|
230 |
}
|
231 |
}
|
232 |
}
|
@@ -236,21 +232,22 @@ abstract class AbstractFrameReflower
|
|
236 |
*
|
237 |
* See http://www.w3.org/TR/CSS21/box.html#collapsing-margins.
|
238 |
*
|
239 |
-
* @param float $
|
240 |
-
* @param float $
|
|
|
241 |
* @return float
|
242 |
*/
|
243 |
-
private function
|
244 |
{
|
245 |
-
if ($
|
246 |
-
return min($
|
247 |
}
|
248 |
|
249 |
-
if ($
|
250 |
-
return $
|
251 |
}
|
252 |
|
253 |
-
return max($
|
254 |
}
|
255 |
|
256 |
/**
|
@@ -275,16 +272,16 @@ abstract class AbstractFrameReflower
|
|
275 |
if ($left === "auto" && $right === "auto") {
|
276 |
$left = 0;
|
277 |
} elseif ($left === "auto") {
|
278 |
-
$left =
|
279 |
}
|
280 |
|
281 |
if ($top === "auto" && $bottom === "auto") {
|
282 |
$top = 0;
|
283 |
} elseif ($top === "auto") {
|
284 |
-
$top =
|
285 |
}
|
286 |
|
287 |
-
$frame->move(
|
288 |
}
|
289 |
}
|
290 |
|
@@ -308,7 +305,7 @@ abstract class AbstractFrameReflower
|
|
308 |
$style = $this->_frame->get_style();
|
309 |
$min_width = $style->min_width;
|
310 |
|
311 |
-
return $min_width !== "auto"
|
312 |
? $style->length_in_pt($min_width, $cbw ?? 0)
|
313 |
: 0.0;
|
314 |
}
|
@@ -328,7 +325,7 @@ abstract class AbstractFrameReflower
|
|
328 |
$style = $this->_frame->get_style();
|
329 |
$max_width = $style->max_width;
|
330 |
|
331 |
-
return $max_width !== "none"
|
332 |
? $style->length_in_pt($max_width, $cbw ?? INF)
|
333 |
: INF;
|
334 |
}
|
@@ -348,7 +345,7 @@ abstract class AbstractFrameReflower
|
|
348 |
$style = $this->_frame->get_style();
|
349 |
$min_height = $style->min_height;
|
350 |
|
351 |
-
return $min_height !== "auto"
|
352 |
? $style->length_in_pt($min_height, $cbh ?? 0)
|
353 |
: 0.0;
|
354 |
}
|
@@ -368,7 +365,7 @@ abstract class AbstractFrameReflower
|
|
368 |
$style = $this->_frame->get_style();
|
369 |
$max_height = $style->max_height;
|
370 |
|
371 |
-
return $max_height !== "none"
|
372 |
? $style->length_in_pt($style->max_height, $cbh ?? INF)
|
373 |
: INF;
|
374 |
}
|
@@ -388,12 +385,13 @@ abstract class AbstractFrameReflower
|
|
388 |
$low = [];
|
389 |
$high = [];
|
390 |
|
391 |
-
for ($iter = $this->_frame->get_children()
|
392 |
$inline_min = 0;
|
393 |
$inline_max = 0;
|
394 |
|
395 |
// Add all adjacent inline widths together to calculate max width
|
396 |
while ($iter->valid() && ($iter->current()->is_inline_level() || $iter->current()->get_style()->display === "-dompdf-image")) {
|
|
|
397 |
$child = $iter->current();
|
398 |
$child->get_reflower()->_set_content();
|
399 |
$minmax = $child->get_min_max_width();
|
@@ -417,6 +415,7 @@ abstract class AbstractFrameReflower
|
|
417 |
|
418 |
// Skip children with absolute position
|
419 |
if ($iter->valid() && !$iter->current()->is_absolute()) {
|
|
|
420 |
$child = $iter->current();
|
421 |
$child->get_reflower()->_set_content();
|
422 |
list($low[], $high[]) = $child->get_min_max_width();
|
@@ -549,7 +548,6 @@ abstract class AbstractFrameReflower
|
|
549 |
*/
|
550 |
protected function _parse_content(): string
|
551 |
{
|
552 |
-
// The `content` property will be returned parsed into its components
|
553 |
$style = $this->_frame->get_style();
|
554 |
$content = $style->content;
|
555 |
|
57 |
$this->_min_max_cache = null;
|
58 |
}
|
59 |
|
|
|
|
|
|
|
|
|
60 |
/**
|
61 |
* @return Dompdf
|
62 |
*/
|
83 |
|
84 |
switch ($style->position) {
|
85 |
case "absolute":
|
86 |
+
$parent = $frame->find_positioned_parent();
|
87 |
if ($parent !== $frame->get_root()) {
|
88 |
$parent_style = $parent->get_style();
|
89 |
$parent_padding_box = $parent->get_padding_box();
|
120 |
* Collapse frames margins
|
121 |
* http://www.w3.org/TR/CSS21/box.html#collapsing-margins
|
122 |
*/
|
123 |
+
protected function _collapse_margins(): void
|
124 |
{
|
125 |
$frame = $this->_frame;
|
126 |
|
139 |
|
140 |
// Handle 'auto' values
|
141 |
if ($t === "auto") {
|
142 |
+
$style->set_used("margin_top", 0.0);
|
143 |
+
$t = 0.0;
|
144 |
}
|
145 |
|
146 |
if ($b === "auto") {
|
147 |
+
$style->set_used("margin_bottom", 0.0);
|
148 |
+
$b = 0.0;
|
149 |
}
|
150 |
|
151 |
// Collapse vertical margins:
|
167 |
$n_style = $n->get_style();
|
168 |
$n_t = (float)$n_style->length_in_pt($n_style->margin_top, $cb["w"]);
|
169 |
|
170 |
+
$b = $this->get_collapsed_margin_length($b, $n_t);
|
171 |
+
$style->set_used("margin_bottom", $b);
|
172 |
+
$n_style->set_used("margin_top", 0.0);
|
173 |
}
|
174 |
|
175 |
// Collapse our first child's margin, if there is no border or padding
|
193 |
$f_style = $f->get_style();
|
194 |
$f_t = (float)$f_style->length_in_pt($f_style->margin_top, $cb["w"]);
|
195 |
|
196 |
+
$t = $this->get_collapsed_margin_length($t, $f_t);
|
197 |
+
$style->set_used("margin_top", $t);
|
198 |
+
$f_style->set_used("margin_top", 0.0);
|
199 |
}
|
200 |
}
|
201 |
|
220 |
$l_style = $l->get_style();
|
221 |
$l_b = (float)$l_style->length_in_pt($l_style->margin_bottom, $cb["w"]);
|
222 |
|
223 |
+
$b = $this->get_collapsed_margin_length($b, $l_b);
|
224 |
+
$style->set_used("margin_bottom", $b);
|
225 |
+
$l_style->set_used("margin_bottom", 0.0);
|
226 |
}
|
227 |
}
|
228 |
}
|
232 |
*
|
233 |
* See http://www.w3.org/TR/CSS21/box.html#collapsing-margins.
|
234 |
*
|
235 |
+
* @param float $l1
|
236 |
+
* @param float $l2
|
237 |
+
*
|
238 |
* @return float
|
239 |
*/
|
240 |
+
private function get_collapsed_margin_length(float $l1, float $l2): float
|
241 |
{
|
242 |
+
if ($l1 < 0 && $l2 < 0) {
|
243 |
+
return min($l1, $l2); // min(x, y) = - max(abs(x), abs(y)), if x < 0 && y < 0
|
244 |
}
|
245 |
|
246 |
+
if ($l1 < 0 || $l2 < 0) {
|
247 |
+
return $l1 + $l2; // x + y = x - abs(y), if y < 0
|
248 |
}
|
249 |
|
250 |
+
return max($l1, $l2);
|
251 |
}
|
252 |
|
253 |
/**
|
272 |
if ($left === "auto" && $right === "auto") {
|
273 |
$left = 0;
|
274 |
} elseif ($left === "auto") {
|
275 |
+
$left = -$right;
|
276 |
}
|
277 |
|
278 |
if ($top === "auto" && $bottom === "auto") {
|
279 |
$top = 0;
|
280 |
} elseif ($top === "auto") {
|
281 |
+
$top = -$bottom;
|
282 |
}
|
283 |
|
284 |
+
$frame->move($left, $top);
|
285 |
}
|
286 |
}
|
287 |
|
305 |
$style = $this->_frame->get_style();
|
306 |
$min_width = $style->min_width;
|
307 |
|
308 |
+
return $min_width !== "auto"
|
309 |
? $style->length_in_pt($min_width, $cbw ?? 0)
|
310 |
: 0.0;
|
311 |
}
|
325 |
$style = $this->_frame->get_style();
|
326 |
$max_width = $style->max_width;
|
327 |
|
328 |
+
return $max_width !== "none"
|
329 |
? $style->length_in_pt($max_width, $cbw ?? INF)
|
330 |
: INF;
|
331 |
}
|
345 |
$style = $this->_frame->get_style();
|
346 |
$min_height = $style->min_height;
|
347 |
|
348 |
+
return $min_height !== "auto"
|
349 |
? $style->length_in_pt($min_height, $cbh ?? 0)
|
350 |
: 0.0;
|
351 |
}
|
365 |
$style = $this->_frame->get_style();
|
366 |
$max_height = $style->max_height;
|
367 |
|
368 |
+
return $max_height !== "none"
|
369 |
? $style->length_in_pt($style->max_height, $cbh ?? INF)
|
370 |
: INF;
|
371 |
}
|
385 |
$low = [];
|
386 |
$high = [];
|
387 |
|
388 |
+
for ($iter = $this->_frame->get_children(); $iter->valid(); $iter->next()) {
|
389 |
$inline_min = 0;
|
390 |
$inline_max = 0;
|
391 |
|
392 |
// Add all adjacent inline widths together to calculate max width
|
393 |
while ($iter->valid() && ($iter->current()->is_inline_level() || $iter->current()->get_style()->display === "-dompdf-image")) {
|
394 |
+
/** @var AbstractFrameDecorator */
|
395 |
$child = $iter->current();
|
396 |
$child->get_reflower()->_set_content();
|
397 |
$minmax = $child->get_min_max_width();
|
415 |
|
416 |
// Skip children with absolute position
|
417 |
if ($iter->valid() && !$iter->current()->is_absolute()) {
|
418 |
+
/** @var AbstractFrameDecorator */
|
419 |
$child = $iter->current();
|
420 |
$child->get_reflower()->_set_content();
|
421 |
list($low[], $high[]) = $child->get_min_max_width();
|
548 |
*/
|
549 |
protected function _parse_content(): string
|
550 |
{
|
|
|
551 |
$style = $this->_frame->get_style();
|
552 |
$content = $style->content;
|
553 |
|
vendor/dompdf/dompdf/src/FrameReflower/Block.php
CHANGED
@@ -603,7 +603,7 @@ class Block extends AbstractFrameReflower
|
|
603 |
$imageHeightDiff = $height * 0.8 - $marginHeight;
|
604 |
|
605 |
$align = $frame->get_style()->vertical_align;
|
606 |
-
if (in_array($align, Style
|
607 |
switch ($align) {
|
608 |
case "middle":
|
609 |
$y_offset = $imageHeightDiff / 2;
|
@@ -644,7 +644,7 @@ class Block extends AbstractFrameReflower
|
|
644 |
} else {
|
645 |
$align = $parent->get_style()->vertical_align;
|
646 |
}
|
647 |
-
if (in_array($align, Style
|
648 |
switch ($align) {
|
649 |
case "middle":
|
650 |
$y_offset = ($height * 0.8 - $baseline) / 2;
|
@@ -808,14 +808,14 @@ class Block extends AbstractFrameReflower
|
|
808 |
|
809 |
// Determine the constraints imposed by this frame: calculate the width
|
810 |
// of the content area:
|
811 |
-
|
812 |
|
813 |
// Store the calculated properties
|
814 |
-
$style->width
|
815 |
-
$style->margin_left
|
816 |
-
$style->margin_right
|
817 |
-
$style->left
|
818 |
-
$style->right
|
819 |
|
820 |
$margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
|
821 |
$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
|
@@ -825,7 +825,7 @@ class Block extends AbstractFrameReflower
|
|
825 |
|
826 |
// Update the position
|
827 |
$this->_frame->position();
|
828 |
-
|
829 |
|
830 |
// Adjust the first line based on the text-indent property
|
831 |
$indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]);
|
@@ -843,7 +843,7 @@ class Block extends AbstractFrameReflower
|
|
843 |
$style->padding_bottom
|
844 |
], $cb["w"]);
|
845 |
|
846 |
-
$cb_x = $x + (float)$
|
847 |
$style->padding_left], $cb["w"]);
|
848 |
|
849 |
$cb_y = $y + $top;
|
@@ -884,12 +884,13 @@ class Block extends AbstractFrameReflower
|
|
884 |
}
|
885 |
|
886 |
// Determine our height
|
887 |
-
|
888 |
-
|
889 |
-
$style->
|
890 |
-
$style->
|
891 |
-
$style->
|
892 |
-
$style->
|
|
|
893 |
|
894 |
if ($this->_frame->is_absolute()) {
|
895 |
if ($auto_top) {
|
603 |
$imageHeightDiff = $height * 0.8 - $marginHeight;
|
604 |
|
605 |
$align = $frame->get_style()->vertical_align;
|
606 |
+
if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
|
607 |
switch ($align) {
|
608 |
case "middle":
|
609 |
$y_offset = $imageHeightDiff / 2;
|
644 |
} else {
|
645 |
$align = $parent->get_style()->vertical_align;
|
646 |
}
|
647 |
+
if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
|
648 |
switch ($align) {
|
649 |
case "middle":
|
650 |
$y_offset = ($height * 0.8 - $baseline) / 2;
|
808 |
|
809 |
// Determine the constraints imposed by this frame: calculate the width
|
810 |
// of the content area:
|
811 |
+
[$width, $margin_left, $margin_right, $left, $right] = $this->_calculate_restricted_width();
|
812 |
|
813 |
// Store the calculated properties
|
814 |
+
$style->set_used("width", $width);
|
815 |
+
$style->set_used("margin_left", $margin_left);
|
816 |
+
$style->set_used("margin_right", $margin_right);
|
817 |
+
$style->set_used("left", $left);
|
818 |
+
$style->set_used("right", $right);
|
819 |
|
820 |
$margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
|
821 |
$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
|
825 |
|
826 |
// Update the position
|
827 |
$this->_frame->position();
|
828 |
+
[$x, $y] = $this->_frame->get_position();
|
829 |
|
830 |
// Adjust the first line based on the text-indent property
|
831 |
$indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]);
|
843 |
$style->padding_bottom
|
844 |
], $cb["w"]);
|
845 |
|
846 |
+
$cb_x = $x + (float)$margin_left + (float)$style->length_in_pt([$style->border_left_width,
|
847 |
$style->padding_left], $cb["w"]);
|
848 |
|
849 |
$cb_y = $y + $top;
|
884 |
}
|
885 |
|
886 |
// Determine our height
|
887 |
+
[$height, $margin_top, $margin_bottom, $top, $bottom] = $this->_calculate_restricted_height();
|
888 |
+
|
889 |
+
$style->set_used("height", $height);
|
890 |
+
$style->set_used("margin_top", $margin_top);
|
891 |
+
$style->set_used("margin_bottom", $margin_bottom);
|
892 |
+
$style->set_used("top", $top);
|
893 |
+
$style->set_used("bottom", $bottom);
|
894 |
|
895 |
if ($this->_frame->is_absolute()) {
|
896 |
if ($auto_top) {
|
vendor/dompdf/dompdf/src/FrameReflower/Image.php
CHANGED
@@ -188,8 +188,8 @@ class Image extends AbstractFrameReflower
|
|
188 |
print $width . " " . $height . ";";
|
189 |
}
|
190 |
|
191 |
-
$style->width
|
192 |
-
$style->height
|
193 |
}
|
194 |
|
195 |
protected function resolve_margins(): void
|
@@ -200,16 +200,16 @@ class Image extends AbstractFrameReflower
|
|
200 |
$style = $this->_frame->get_style();
|
201 |
|
202 |
if ($style->margin_left === "auto") {
|
203 |
-
$style->margin_left
|
204 |
}
|
205 |
if ($style->margin_right === "auto") {
|
206 |
-
$style->margin_right
|
207 |
}
|
208 |
if ($style->margin_top === "auto") {
|
209 |
-
$style->margin_top
|
210 |
}
|
211 |
if ($style->margin_bottom === "auto") {
|
212 |
-
$style->margin_bottom
|
213 |
}
|
214 |
}
|
215 |
}
|
188 |
print $width . " " . $height . ";";
|
189 |
}
|
190 |
|
191 |
+
$style->set_used("width", $width);
|
192 |
+
$style->set_used("height", $height);
|
193 |
}
|
194 |
|
195 |
protected function resolve_margins(): void
|
200 |
$style = $this->_frame->get_style();
|
201 |
|
202 |
if ($style->margin_left === "auto") {
|
203 |
+
$style->set_used("margin_left", 0.0);
|
204 |
}
|
205 |
if ($style->margin_right === "auto") {
|
206 |
+
$style->set_used("margin_right", 0.0);
|
207 |
}
|
208 |
if ($style->margin_top === "auto") {
|
209 |
+
$style->set_used("margin_top", 0.0);
|
210 |
}
|
211 |
if ($style->margin_bottom === "auto") {
|
212 |
+
$style->set_used("margin_bottom", 0.0);
|
213 |
}
|
214 |
}
|
215 |
}
|
vendor/dompdf/dompdf/src/FrameReflower/Inline.php
CHANGED
@@ -43,7 +43,7 @@ class Inline extends AbstractFrameReflower
|
|
43 |
$style = $frame->get_style();
|
44 |
|
45 |
// Resolve width, so the margin width can be checked
|
46 |
-
$style->width
|
47 |
|
48 |
$cb = $frame->get_containing_block();
|
49 |
$line = $block->get_current_line_box();
|
@@ -97,16 +97,16 @@ class Inline extends AbstractFrameReflower
|
|
97 |
// https://www.w3.org/TR/CSS21/visudet.html#inline-width
|
98 |
// https://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced
|
99 |
if ($style->margin_left === "auto") {
|
100 |
-
$style->margin_left
|
101 |
}
|
102 |
if ($style->margin_right === "auto") {
|
103 |
-
$style->margin_right
|
104 |
}
|
105 |
if ($style->margin_top === "auto") {
|
106 |
-
$style->margin_top
|
107 |
}
|
108 |
if ($style->margin_bottom === "auto") {
|
109 |
-
$style->margin_bottom
|
110 |
}
|
111 |
|
112 |
// Handle line breaks
|
@@ -140,14 +140,18 @@ class Inline extends AbstractFrameReflower
|
|
140 |
$f_style = $f->get_style();
|
141 |
$f_style->margin_left = $style->margin_left;
|
142 |
$f_style->padding_left = $style->padding_left;
|
143 |
-
$f_style->
|
|
|
|
|
144 |
}
|
145 |
|
146 |
if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) {
|
147 |
$l_style = $l->get_style();
|
148 |
$l_style->margin_right = $style->margin_right;
|
149 |
$l_style->padding_right = $style->padding_right;
|
150 |
-
$l_style->
|
|
|
|
|
151 |
}
|
152 |
|
153 |
$cb = $frame->get_containing_block();
|
43 |
$style = $frame->get_style();
|
44 |
|
45 |
// Resolve width, so the margin width can be checked
|
46 |
+
$style->set_used("width", 0.0);
|
47 |
|
48 |
$cb = $frame->get_containing_block();
|
49 |
$line = $block->get_current_line_box();
|
97 |
// https://www.w3.org/TR/CSS21/visudet.html#inline-width
|
98 |
// https://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced
|
99 |
if ($style->margin_left === "auto") {
|
100 |
+
$style->set_used("margin_left", 0.0);
|
101 |
}
|
102 |
if ($style->margin_right === "auto") {
|
103 |
+
$style->set_used("margin_right", 0.0);
|
104 |
}
|
105 |
if ($style->margin_top === "auto") {
|
106 |
+
$style->set_used("margin_top", 0.0);
|
107 |
}
|
108 |
if ($style->margin_bottom === "auto") {
|
109 |
+
$style->set_used("margin_bottom", 0.0);
|
110 |
}
|
111 |
|
112 |
// Handle line breaks
|
140 |
$f_style = $f->get_style();
|
141 |
$f_style->margin_left = $style->margin_left;
|
142 |
$f_style->padding_left = $style->padding_left;
|
143 |
+
$f_style->border_left_width = $style->border_left_width;
|
144 |
+
$f_style->border_left_style = $style->border_left_style;
|
145 |
+
$f_style->border_left_color = $style->border_left_color;
|
146 |
}
|
147 |
|
148 |
if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) {
|
149 |
$l_style = $l->get_style();
|
150 |
$l_style->margin_right = $style->margin_right;
|
151 |
$l_style->padding_right = $style->padding_right;
|
152 |
+
$l_style->border_right_width = $style->border_right_width;
|
153 |
+
$l_style->border_right_style = $style->border_right_style;
|
154 |
+
$l_style->border_right_color = $style->border_right_color;
|
155 |
}
|
156 |
|
157 |
$cb = $frame->get_containing_block();
|
vendor/dompdf/dompdf/src/FrameReflower/ListBullet.php
CHANGED
@@ -36,7 +36,7 @@ class ListBullet extends AbstractFrameReflower
|
|
36 |
$frame = $this->_frame;
|
37 |
$style = $frame->get_style();
|
38 |
|
39 |
-
$style->width
|
40 |
$frame->position();
|
41 |
|
42 |
if ($style->list_style_position === "inside") {
|
36 |
$frame = $this->_frame;
|
37 |
$style = $frame->get_style();
|
38 |
|
39 |
+
$style->set_used("width", $frame->get_width());
|
40 |
$frame->position();
|
41 |
|
42 |
if ($style->list_style_position === "inside") {
|
vendor/dompdf/dompdf/src/FrameReflower/Page.php
CHANGED
@@ -119,8 +119,7 @@ class Page extends AbstractFrameReflower
|
|
119 |
|
120 |
// Only if it's the first page, we save the nodes with a fixed position
|
121 |
if ($current_page == 0) {
|
122 |
-
|
123 |
-
foreach ($children as $onechild) {
|
124 |
if ($onechild->get_style()->position === "fixed") {
|
125 |
$fixed_children[] = $onechild->deep_copy();
|
126 |
}
|
@@ -182,20 +181,18 @@ class Page extends AbstractFrameReflower
|
|
182 |
protected function _check_callbacks(string $event, Frame $frame): void
|
183 |
{
|
184 |
if (!isset($this->_callbacks)) {
|
185 |
-
$dompdf = $this->
|
186 |
$this->_callbacks = $dompdf->getCallbacks();
|
187 |
$this->_canvas = $dompdf->getCanvas();
|
188 |
}
|
189 |
|
190 |
if (isset($this->_callbacks[$event])) {
|
191 |
$fs = $this->_callbacks[$event];
|
192 |
-
$
|
193 |
-
|
194 |
-
1 => $frame, "frame" => $frame,
|
195 |
-
];
|
196 |
|
197 |
foreach ($fs as $f) {
|
198 |
-
$f($
|
199 |
}
|
200 |
}
|
201 |
}
|
119 |
|
120 |
// Only if it's the first page, we save the nodes with a fixed position
|
121 |
if ($current_page == 0) {
|
122 |
+
foreach ($child->get_children() as $onechild) {
|
|
|
123 |
if ($onechild->get_style()->position === "fixed") {
|
124 |
$fixed_children[] = $onechild->deep_copy();
|
125 |
}
|
181 |
protected function _check_callbacks(string $event, Frame $frame): void
|
182 |
{
|
183 |
if (!isset($this->_callbacks)) {
|
184 |
+
$dompdf = $this->get_dompdf();
|
185 |
$this->_callbacks = $dompdf->getCallbacks();
|
186 |
$this->_canvas = $dompdf->getCanvas();
|
187 |
}
|
188 |
|
189 |
if (isset($this->_callbacks[$event])) {
|
190 |
$fs = $this->_callbacks[$event];
|
191 |
+
$canvas = $this->_canvas;
|
192 |
+
$fontMetrics = $this->get_dompdf()->getFontMetrics();
|
|
|
|
|
193 |
|
194 |
foreach ($fs as $f) {
|
195 |
+
$f($frame, $canvas, $fontMetrics);
|
196 |
}
|
197 |
}
|
198 |
}
|
vendor/dompdf/dompdf/src/FrameReflower/Table.php
CHANGED
@@ -93,7 +93,7 @@ class Table extends AbstractFrameReflower
|
|
93 |
} else {
|
94 |
if ($max_width + $delta < $cb["w"]) {
|
95 |
$width = $max_width;
|
96 |
-
}
|
97 |
$width = $cb["w"] - $delta;
|
98 |
} else {
|
99 |
$width = $min_width;
|
@@ -106,7 +106,7 @@ class Table extends AbstractFrameReflower
|
|
106 |
}
|
107 |
|
108 |
// Store our resolved width
|
109 |
-
$style->width
|
110 |
|
111 |
$cellmap = $this->_frame->get_cellmap();
|
112 |
|
@@ -116,8 +116,8 @@ class Table extends AbstractFrameReflower
|
|
116 |
|
117 |
// If the whole table fits on the page, then assign each column it's max width
|
118 |
if ($width == $max_width) {
|
119 |
-
foreach (
|
120 |
-
$cellmap->set_column_width($i, $
|
121 |
}
|
122 |
|
123 |
return;
|
@@ -179,10 +179,9 @@ class Table extends AbstractFrameReflower
|
|
179 |
$increment = $width - $absolute_used;
|
180 |
|
181 |
foreach ($absolute as $i) {
|
182 |
-
$
|
183 |
-
$abs = $columns[$i]["absolute"];
|
184 |
$f = $absolute_used > 0 ? $abs / $absolute_used : 1 / count($absolute);
|
185 |
-
$w = $
|
186 |
$cellmap->set_column_width($i, $w);
|
187 |
}
|
188 |
return;
|
@@ -246,8 +245,8 @@ class Table extends AbstractFrameReflower
|
|
246 |
} else {
|
247 |
// We are over-constrained:
|
248 |
// Each column gets its minimum width
|
249 |
-
foreach (
|
250 |
-
$cellmap->set_column_width($i, $
|
251 |
}
|
252 |
}
|
253 |
}
|
@@ -341,15 +340,14 @@ class Table extends AbstractFrameReflower
|
|
341 |
// border-spacing to the table as padding. The other half is added to
|
342 |
// the cells themselves.
|
343 |
if ($style->border_collapse === "separate") {
|
344 |
-
|
345 |
-
|
346 |
-
$
|
347 |
-
|
348 |
-
|
349 |
-
$style->
|
350 |
-
$style->
|
351 |
-
$style->
|
352 |
-
$style->padding_bottom = (float)$style->length_in_pt($style->padding_bottom, $cb["w"]) + $v;
|
353 |
}
|
354 |
|
355 |
$this->_assign_widths();
|
@@ -378,11 +376,11 @@ class Table extends AbstractFrameReflower
|
|
378 |
}
|
379 |
}
|
380 |
|
381 |
-
$style->margin_left
|
382 |
-
$style->margin_right
|
383 |
|
384 |
$frame->position();
|
385 |
-
|
386 |
|
387 |
// Determine the content edge
|
388 |
$offset_x = (float)$left + (float)$style->length_in_pt([
|
@@ -435,7 +433,7 @@ class Table extends AbstractFrameReflower
|
|
435 |
}
|
436 |
|
437 |
// Assign heights to our cells:
|
438 |
-
$style->height
|
439 |
|
440 |
$page->table_reflow_end();
|
441 |
|
@@ -478,19 +476,19 @@ class Table extends AbstractFrameReflower
|
|
478 |
$this->_state["auto"] = [];
|
479 |
|
480 |
$columns =& $cellmap->get_columns();
|
481 |
-
foreach (
|
482 |
-
$this->_state["min_width"] += $
|
483 |
-
$this->_state["max_width"] += $
|
484 |
|
485 |
-
if ($
|
486 |
$this->_state["absolute"][] = $i;
|
487 |
-
$this->_state["absolute_used"] += $
|
488 |
-
}
|
489 |
$this->_state["percent"][] = $i;
|
490 |
-
$this->_state["percent_used"] += $
|
491 |
} else {
|
492 |
$this->_state["auto"][] = $i;
|
493 |
-
$this->_state["auto_min"] += $
|
494 |
}
|
495 |
}
|
496 |
|
93 |
} else {
|
94 |
if ($max_width + $delta < $cb["w"]) {
|
95 |
$width = $max_width;
|
96 |
+
} elseif ($cb["w"] - $delta > $min_width) {
|
97 |
$width = $cb["w"] - $delta;
|
98 |
} else {
|
99 |
$width = $min_width;
|
106 |
}
|
107 |
|
108 |
// Store our resolved width
|
109 |
+
$style->set_used("width", $width);
|
110 |
|
111 |
$cellmap = $this->_frame->get_cellmap();
|
112 |
|
116 |
|
117 |
// If the whole table fits on the page, then assign each column it's max width
|
118 |
if ($width == $max_width) {
|
119 |
+
foreach ($columns as $i => $col) {
|
120 |
+
$cellmap->set_column_width($i, $col["max-width"]);
|
121 |
}
|
122 |
|
123 |
return;
|
179 |
$increment = $width - $absolute_used;
|
180 |
|
181 |
foreach ($absolute as $i) {
|
182 |
+
$abs = $columns[$i]["min-width"];
|
|
|
183 |
$f = $absolute_used > 0 ? $abs / $absolute_used : 1 / count($absolute);
|
184 |
+
$w = $abs + $increment * $f;
|
185 |
$cellmap->set_column_width($i, $w);
|
186 |
}
|
187 |
return;
|
245 |
} else {
|
246 |
// We are over-constrained:
|
247 |
// Each column gets its minimum width
|
248 |
+
foreach ($columns as $i => $col) {
|
249 |
+
$cellmap->set_column_width($i, $col["min-width"]);
|
250 |
}
|
251 |
}
|
252 |
}
|
340 |
// border-spacing to the table as padding. The other half is added to
|
341 |
// the cells themselves.
|
342 |
if ($style->border_collapse === "separate") {
|
343 |
+
[$h, $v] = $style->border_spacing;
|
344 |
+
$v = $v / 2;
|
345 |
+
$h = $h / 2;
|
346 |
+
|
347 |
+
$style->set_used("padding_left", (float)$style->length_in_pt($style->padding_left, $cb["w"]) + $h);
|
348 |
+
$style->set_used("padding_right", (float)$style->length_in_pt($style->padding_right, $cb["w"]) + $h);
|
349 |
+
$style->set_used("padding_top", (float)$style->length_in_pt($style->padding_top, $cb["w"]) + $v);
|
350 |
+
$style->set_used("padding_bottom", (float)$style->length_in_pt($style->padding_bottom, $cb["w"]) + $v);
|
|
|
351 |
}
|
352 |
|
353 |
$this->_assign_widths();
|
376 |
}
|
377 |
}
|
378 |
|
379 |
+
$style->set_used("margin_left", $left);
|
380 |
+
$style->set_used("margin_right", $right);
|
381 |
|
382 |
$frame->position();
|
383 |
+
[$x, $y] = $frame->get_position();
|
384 |
|
385 |
// Determine the content edge
|
386 |
$offset_x = (float)$left + (float)$style->length_in_pt([
|
433 |
}
|
434 |
|
435 |
// Assign heights to our cells:
|
436 |
+
$style->set_used("height", $this->_calculate_height());
|
437 |
|
438 |
$page->table_reflow_end();
|
439 |
|
476 |
$this->_state["auto"] = [];
|
477 |
|
478 |
$columns =& $cellmap->get_columns();
|
479 |
+
foreach ($columns as $i => $col) {
|
480 |
+
$this->_state["min_width"] += $col["min-width"];
|
481 |
+
$this->_state["max_width"] += $col["max-width"];
|
482 |
|
483 |
+
if ($col["absolute"] > 0) {
|
484 |
$this->_state["absolute"][] = $i;
|
485 |
+
$this->_state["absolute_used"] += $col["min-width"];
|
486 |
+
} elseif ($col["percent"] > 0) {
|
487 |
$this->_state["percent"][] = $i;
|
488 |
+
$this->_state["percent_used"] += $col["percent"];
|
489 |
} else {
|
490 |
$this->_state["auto"][] = $i;
|
491 |
+
$this->_state["auto_min"] += $col["min-width"];
|
492 |
}
|
493 |
}
|
494 |
|
vendor/dompdf/dompdf/src/FrameReflower/TableCell.php
CHANGED
@@ -73,7 +73,8 @@ class TableCell extends Block
|
|
73 |
$style->border_bottom_width],
|
74 |
$h);
|
75 |
|
76 |
-
$
|
|
|
77 |
|
78 |
$content_x = $x + $left_space;
|
79 |
$content_y = $line_y = $y + $top_space;
|
@@ -118,7 +119,8 @@ class TableCell extends Block
|
|
118 |
$cellmap->set_row_height($i, $cell_height);
|
119 |
}
|
120 |
|
121 |
-
$style->height
|
|
|
122 |
$this->_text_align();
|
123 |
$this->vertical_align();
|
124 |
|
73 |
$style->border_bottom_width],
|
74 |
$h);
|
75 |
|
76 |
+
$cb_w = $w - $left_space - $right_space;
|
77 |
+
$style->set_used("width", $cb_w);
|
78 |
|
79 |
$content_x = $x + $left_space;
|
80 |
$content_y = $line_y = $y + $top_space;
|
119 |
$cellmap->set_row_height($i, $cell_height);
|
120 |
}
|
121 |
|
122 |
+
$style->set_used("height", $height);
|
123 |
+
|
124 |
$this->_text_align();
|
125 |
$this->vertical_align();
|
126 |
|
vendor/dompdf/dompdf/src/FrameReflower/TableRow.php
CHANGED
@@ -67,8 +67,8 @@ class TableRow extends AbstractFrameReflower
|
|
67 |
|
68 |
$table = TableFrameDecorator::find_parent_table($this->_frame);
|
69 |
$cellmap = $table->get_cellmap();
|
70 |
-
$style->width
|
71 |
-
$style->height
|
72 |
|
73 |
$this->_frame->set_position($cellmap->get_frame_position($this->_frame));
|
74 |
}
|
67 |
|
68 |
$table = TableFrameDecorator::find_parent_table($this->_frame);
|
69 |
$cellmap = $table->get_cellmap();
|
70 |
+
$style->set_used("width", $cellmap->get_frame_width($this->_frame));
|
71 |
+
$style->set_used("height", $cellmap->get_frame_height($this->_frame));
|
72 |
|
73 |
$this->_frame->set_position($cellmap->get_frame_position($this->_frame));
|
74 |
}
|
vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php
CHANGED
@@ -64,8 +64,8 @@ class TableRowGroup extends AbstractFrameReflower
|
|
64 |
return;
|
65 |
}
|
66 |
|
67 |
-
$style->width
|
68 |
-
$style->height
|
69 |
|
70 |
$frame->set_position($cellmap->get_frame_position($frame));
|
71 |
}
|
64 |
return;
|
65 |
}
|
66 |
|
67 |
+
$style->set_used("width", $cellmap->get_frame_width($frame));
|
68 |
+
$style->set_used("height", $cellmap->get_frame_height($frame));
|
69 |
|
70 |
$frame->set_position($cellmap->get_frame_position($frame));
|
71 |
}
|
vendor/dompdf/dompdf/src/FrameReflower/Text.php
CHANGED
@@ -129,25 +129,23 @@ class Text extends AbstractFrameReflower
|
|
129 |
*/
|
130 |
protected function line_break(string $text, BlockFrameDecorator $block)
|
131 |
{
|
|
|
132 |
$frame = $this->_frame;
|
133 |
$style = $frame->get_style();
|
134 |
-
$size = $style->font_size;
|
135 |
$font = $style->font_family;
|
136 |
-
$
|
137 |
-
$
|
|
|
138 |
|
139 |
// Determine the available width
|
|
|
140 |
$line_width = $frame->get_containing_block("w");
|
141 |
$current_line_width = $current_line->left + $current_line->w + $current_line->right;
|
142 |
$available_width = $line_width - $current_line_width;
|
143 |
|
144 |
-
// Account for word and letter spacing
|
145 |
-
$word_spacing = (float) $style->length_in_pt($style->word_spacing);
|
146 |
-
$char_spacing = (float) $style->length_in_pt($style->letter_spacing);
|
147 |
-
|
148 |
// Determine the frame width including margin, padding & border
|
149 |
$visible_text = preg_replace('/\xAD/u', "", $text);
|
150 |
-
$text_width = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $
|
151 |
$mbp_width = (float) $style->length_in_pt([
|
152 |
$style->margin_left,
|
153 |
$style->border_left_width,
|
@@ -170,7 +168,7 @@ class Text extends AbstractFrameReflower
|
|
170 |
$width = 0;
|
171 |
$str = "";
|
172 |
|
173 |
-
$space_width = $fontMetrics->getTextWidth(" ", $font, $size, $word_spacing, $
|
174 |
$shy_width = $fontMetrics->getTextWidth(self::SOFT_HYPHEN, $font, $size);
|
175 |
|
176 |
// @todo support <wbr>
|
@@ -180,7 +178,7 @@ class Text extends AbstractFrameReflower
|
|
180 |
// handle that for now
|
181 |
$sep = $words[$i + 1] ?? "";
|
182 |
$word = $sep === " " ? $words[$i] : $words[$i] . $sep;
|
183 |
-
$word_width = $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $
|
184 |
$used_width = $width + $word_width + $mbp_width;
|
185 |
|
186 |
if (Helpers::lengthGreater($used_width, $available_width)) {
|
@@ -224,7 +222,7 @@ class Text extends AbstractFrameReflower
|
|
224 |
|
225 |
for ($j = 0; $j < mb_strlen($word); $j++) {
|
226 |
$c = mb_substr($word, $j, 1);
|
227 |
-
$w = $fontMetrics->getTextWidth($s . $c, $font, $size, $word_spacing, $
|
228 |
|
229 |
if (Helpers::lengthGreater($w, $available_width)) {
|
230 |
break;
|
@@ -278,7 +276,7 @@ class Text extends AbstractFrameReflower
|
|
278 |
// elements in case white space is collapsed
|
279 |
if ($text === "") {
|
280 |
$frame->set_text("");
|
281 |
-
$style->width
|
282 |
return null;
|
283 |
}
|
284 |
|
@@ -391,7 +389,8 @@ class Text extends AbstractFrameReflower
|
|
391 |
$style = $frame->get_style();
|
392 |
$size = $style->font_size;
|
393 |
$font = $style->font_family;
|
394 |
-
$
|
|
|
395 |
|
396 |
// Handle text transform and white space
|
397 |
$text = $this->pre_process_text($frame->get_text());
|
@@ -457,12 +456,14 @@ class Text extends AbstractFrameReflower
|
|
457 |
|
458 |
public function get_min_max_width(): array
|
459 |
{
|
|
|
460 |
$frame = $this->_frame;
|
461 |
$style = $frame->get_style();
|
462 |
-
$size = $style->font_size;
|
463 |
-
$font = $style->font_family;
|
464 |
$text = $frame->get_text();
|
465 |
-
$
|
|
|
|
|
|
|
466 |
|
467 |
// Handle text transform and white space
|
468 |
$text = $this->pre_process_text($frame->get_text());
|
@@ -498,10 +499,6 @@ class Text extends AbstractFrameReflower
|
|
498 |
// Strip soft hyphens for max-line-width calculations
|
499 |
$visible_text = preg_replace('/\xAD/u', "", $text);
|
500 |
|
501 |
-
// Account for word and letter spacing
|
502 |
-
$word_spacing = (float) $style->length_in_pt($style->word_spacing);
|
503 |
-
$char_spacing = (float) $style->length_in_pt($style->letter_spacing);
|
504 |
-
|
505 |
// Determine minimum text width
|
506 |
switch ($style->white_space) {
|
507 |
default:
|
@@ -515,16 +512,16 @@ class Text extends AbstractFrameReflower
|
|
515 |
// https://www.w3.org/TR/css-text-3/#overflow-wrap-property
|
516 |
if ($style->overflow_wrap === "anywhere") {
|
517 |
$char = mb_substr($visible_text, 0, 1);
|
518 |
-
$min = $fontMetrics->getTextWidth($char, $font, $size, $word_spacing, $
|
519 |
} else {
|
520 |
// Find the longest word
|
521 |
$words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
522 |
-
$lengths = array_map(function ($chunk) use ($fontMetrics, $font, $size, $word_spacing, $
|
523 |
// Allow trailing white space to overflow. As in actual
|
524 |
// layout above, only handle a single space for now
|
525 |
$sep = $chunk[1] ?? "";
|
526 |
$word = $sep === " " ? $chunk[0] : $chunk[0] . $sep;
|
527 |
-
return $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $
|
528 |
}, array_chunk($words, 2));
|
529 |
$min = max($lengths);
|
530 |
}
|
@@ -533,15 +530,15 @@ class Text extends AbstractFrameReflower
|
|
533 |
case "pre":
|
534 |
// Find the longest line
|
535 |
$lines = array_flip(preg_split("/\R/u", $visible_text));
|
536 |
-
array_walk($lines, function(&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $
|
537 |
-
$chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $
|
538 |
});
|
539 |
arsort($lines);
|
540 |
$min = reset($lines);
|
541 |
break;
|
542 |
|
543 |
case "nowrap":
|
544 |
-
$min = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $
|
545 |
break;
|
546 |
}
|
547 |
|
@@ -549,15 +546,15 @@ class Text extends AbstractFrameReflower
|
|
549 |
switch ($style->white_space) {
|
550 |
default:
|
551 |
case "normal":
|
552 |
-
$max = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $
|
553 |
break;
|
554 |
|
555 |
case "pre-line":
|
556 |
case "pre-wrap":
|
557 |
// Find the longest line
|
558 |
$lines = array_flip(preg_split("/\R/u", $visible_text));
|
559 |
-
array_walk($lines, function(&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $
|
560 |
-
$chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $
|
561 |
});
|
562 |
arsort($lines);
|
563 |
$max = reset($lines);
|
129 |
*/
|
130 |
protected function line_break(string $text, BlockFrameDecorator $block)
|
131 |
{
|
132 |
+
$fontMetrics = $this->getFontMetrics();
|
133 |
$frame = $this->_frame;
|
134 |
$style = $frame->get_style();
|
|
|
135 |
$font = $style->font_family;
|
136 |
+
$size = $style->font_size;
|
137 |
+
$word_spacing = $style->word_spacing;
|
138 |
+
$letter_spacing = $style->letter_spacing;
|
139 |
|
140 |
// Determine the available width
|
141 |
+
$current_line = $block->get_current_line_box();
|
142 |
$line_width = $frame->get_containing_block("w");
|
143 |
$current_line_width = $current_line->left + $current_line->w + $current_line->right;
|
144 |
$available_width = $line_width - $current_line_width;
|
145 |
|
|
|
|
|
|
|
|
|
146 |
// Determine the frame width including margin, padding & border
|
147 |
$visible_text = preg_replace('/\xAD/u', "", $text);
|
148 |
+
$text_width = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
|
149 |
$mbp_width = (float) $style->length_in_pt([
|
150 |
$style->margin_left,
|
151 |
$style->border_left_width,
|
168 |
$width = 0;
|
169 |
$str = "";
|
170 |
|
171 |
+
$space_width = $fontMetrics->getTextWidth(" ", $font, $size, $word_spacing, $letter_spacing);
|
172 |
$shy_width = $fontMetrics->getTextWidth(self::SOFT_HYPHEN, $font, $size);
|
173 |
|
174 |
// @todo support <wbr>
|
178 |
// handle that for now
|
179 |
$sep = $words[$i + 1] ?? "";
|
180 |
$word = $sep === " " ? $words[$i] : $words[$i] . $sep;
|
181 |
+
$word_width = $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
|
182 |
$used_width = $width + $word_width + $mbp_width;
|
183 |
|
184 |
if (Helpers::lengthGreater($used_width, $available_width)) {
|
222 |
|
223 |
for ($j = 0; $j < mb_strlen($word); $j++) {
|
224 |
$c = mb_substr($word, $j, 1);
|
225 |
+
$w = $fontMetrics->getTextWidth($s . $c, $font, $size, $word_spacing, $letter_spacing);
|
226 |
|
227 |
if (Helpers::lengthGreater($w, $available_width)) {
|
228 |
break;
|
276 |
// elements in case white space is collapsed
|
277 |
if ($text === "") {
|
278 |
$frame->set_text("");
|
279 |
+
$style->set_used("width", 0.0);
|
280 |
return null;
|
281 |
}
|
282 |
|
389 |
$style = $frame->get_style();
|
390 |
$size = $style->font_size;
|
391 |
$font = $style->font_family;
|
392 |
+
$font_height = $this->getFontMetrics()->getFontHeight($font, $size);
|
393 |
+
$style->set_used("height", $font_height);
|
394 |
|
395 |
// Handle text transform and white space
|
396 |
$text = $this->pre_process_text($frame->get_text());
|
456 |
|
457 |
public function get_min_max_width(): array
|
458 |
{
|
459 |
+
$fontMetrics = $this->getFontMetrics();
|
460 |
$frame = $this->_frame;
|
461 |
$style = $frame->get_style();
|
|
|
|
|
462 |
$text = $frame->get_text();
|
463 |
+
$font = $style->font_family;
|
464 |
+
$size = $style->font_size;
|
465 |
+
$word_spacing = $style->word_spacing;
|
466 |
+
$letter_spacing = $style->letter_spacing;
|
467 |
|
468 |
// Handle text transform and white space
|
469 |
$text = $this->pre_process_text($frame->get_text());
|
499 |
// Strip soft hyphens for max-line-width calculations
|
500 |
$visible_text = preg_replace('/\xAD/u', "", $text);
|
501 |
|
|
|
|
|
|
|
|
|
502 |
// Determine minimum text width
|
503 |
switch ($style->white_space) {
|
504 |
default:
|
512 |
// https://www.w3.org/TR/css-text-3/#overflow-wrap-property
|
513 |
if ($style->overflow_wrap === "anywhere") {
|
514 |
$char = mb_substr($visible_text, 0, 1);
|
515 |
+
$min = $fontMetrics->getTextWidth($char, $font, $size, $word_spacing, $letter_spacing);
|
516 |
} else {
|
517 |
// Find the longest word
|
518 |
$words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
519 |
+
$lengths = array_map(function ($chunk) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
|
520 |
// Allow trailing white space to overflow. As in actual
|
521 |
// layout above, only handle a single space for now
|
522 |
$sep = $chunk[1] ?? "";
|
523 |
$word = $sep === " " ? $chunk[0] : $chunk[0] . $sep;
|
524 |
+
return $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
|
525 |
}, array_chunk($words, 2));
|
526 |
$min = max($lengths);
|
527 |
}
|
530 |
case "pre":
|
531 |
// Find the longest line
|
532 |
$lines = array_flip(preg_split("/\R/u", $visible_text));
|
533 |
+
array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
|
534 |
+
$chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
|
535 |
});
|
536 |
arsort($lines);
|
537 |
$min = reset($lines);
|
538 |
break;
|
539 |
|
540 |
case "nowrap":
|
541 |
+
$min = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
|
542 |
break;
|
543 |
}
|
544 |
|
546 |
switch ($style->white_space) {
|
547 |
default:
|
548 |
case "normal":
|
549 |
+
$max = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
|
550 |
break;
|
551 |
|
552 |
case "pre-line":
|
553 |
case "pre-wrap":
|
554 |
// Find the longest line
|
555 |
$lines = array_flip(preg_split("/\R/u", $visible_text));
|
556 |
+
array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
|
557 |
+
$chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
|
558 |
});
|
559 |
arsort($lines);
|
560 |
$max = reset($lines);
|
vendor/dompdf/dompdf/src/Helpers.php
CHANGED
@@ -57,28 +57,45 @@ class Helpers
|
|
57 |
public static function build_url($protocol, $host, $base_path, $url)
|
58 |
{
|
59 |
$protocol = mb_strtolower($protocol);
|
|
|
|
|
|
|
60 |
if ($url === "") {
|
61 |
-
|
62 |
-
return $protocol . $host . $base_path;
|
63 |
}
|
64 |
|
|
|
|
|
65 |
// Is the url already fully qualified, a Data URI, or a reference to a named anchor?
|
66 |
// File-protocol URLs may require additional processing (e.g. for URLs with a relative path)
|
67 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
return $url;
|
69 |
}
|
70 |
|
71 |
-
|
|
|
72 |
$url = substr($url, 7);
|
73 |
-
$protocol = "";
|
|
|
|
|
|
|
|
|
74 |
}
|
75 |
|
76 |
$ret = "";
|
77 |
-
if ($protocol !== "file://") {
|
78 |
-
$ret = $protocol;
|
79 |
-
}
|
80 |
|
81 |
-
|
|
|
|
|
82 |
//On Windows local file, an abs path can begin also with a '\' or a drive letter and colon
|
83 |
//drive: followed by a relative path would be a drive specific default folder.
|
84 |
//not known in php app code, treat as abs path
|
@@ -89,9 +106,18 @@ class Helpers
|
|
89 |
}
|
90 |
$ret .= $url;
|
91 |
$ret = preg_replace('/\?(.*)$/', "", $ret);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
return $ret;
|
93 |
}
|
94 |
|
|
|
95 |
// Protocol relative urls (e.g. "//example.org/style.css")
|
96 |
if (strpos($url, '//') === 0) {
|
97 |
$ret .= substr($url, 2);
|
@@ -431,14 +457,14 @@ class Helpers
|
|
431 |
$host = "";
|
432 |
$path = "";
|
433 |
$file = "";
|
|
|
434 |
|
435 |
$arr = parse_url($url);
|
436 |
if ( isset($arr["scheme"]) ) {
|
437 |
$arr["scheme"] = mb_strtolower($arr["scheme"]);
|
438 |
}
|
439 |
|
440 |
-
|
441 |
-
if (isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1) {
|
442 |
$protocol = $arr["scheme"] . "://";
|
443 |
|
444 |
if (isset($arr["user"])) {
|
@@ -480,42 +506,32 @@ class Helpers
|
|
480 |
|
481 |
} else {
|
482 |
|
483 |
-
$
|
484 |
-
if ($i !== false) {
|
485 |
-
$url = mb_substr($url, $i + 7);
|
486 |
-
}
|
487 |
-
|
488 |
-
$protocol = ""; // "file://"; ? why doesn't this work... It's because of
|
489 |
-
// network filenames like //COMPU/SHARENAME
|
490 |
-
|
491 |
$host = ""; // localhost, really
|
492 |
-
$file = basename($url);
|
493 |
-
|
494 |
-
$path = dirname($url);
|
495 |
-
|
496 |
-
// Check that the path exists
|
497 |
-
if ($path !== false) {
|
498 |
-
$path .= '/';
|
499 |
|
|
|
|
|
|
|
|
|
500 |
} else {
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
$host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n");
|
505 |
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
$path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"];
|
510 |
-
}
|
511 |
}
|
|
|
|
|
|
|
512 |
}
|
513 |
|
514 |
$ret = [$protocol, $host, $path, $file,
|
515 |
"protocol" => $protocol,
|
516 |
"host" => $host,
|
517 |
"path" => $path,
|
518 |
-
"file" => $file
|
|
|
519 |
return $ret;
|
520 |
}
|
521 |
|
@@ -576,12 +592,12 @@ class Helpers
|
|
576 |
{
|
577 |
if ($c <= 0x7F) {
|
578 |
return chr($c);
|
579 |
-
}
|
580 |
return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
|
581 |
-
}
|
582 |
return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
|
583 |
. chr(0x80 | $c & 0x3F);
|
584 |
-
}
|
585 |
return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
|
586 |
. chr(0x80 | $c >> 6 & 0x3F)
|
587 |
. chr(0x80 | $c & 0x3F);
|
@@ -868,9 +884,9 @@ class Helpers
|
|
868 |
* - curl: if allow_url_fopen is false and curl is available
|
869 |
*
|
870 |
* @param string $uri
|
871 |
-
* @param resource $context
|
872 |
* @param int $offset
|
873 |
-
* @param int $maxlen
|
874 |
* @return string[]
|
875 |
*/
|
876 |
public static function getFileContent($uri, $context = null, $offset = 0, $maxlen = null)
|
@@ -878,12 +894,13 @@ class Helpers
|
|
878 |
$content = null;
|
879 |
$headers = null;
|
880 |
[$protocol] = Helpers::explode_url($uri);
|
881 |
-
$is_local_path = ($protocol
|
|
|
882 |
|
883 |
set_error_handler([self::class, 'record_warnings']);
|
884 |
|
885 |
try {
|
886 |
-
if ($is_local_path || ini_get('allow_url_fopen')) {
|
887 |
if ($is_local_path === false) {
|
888 |
$uri = Helpers::encodeURI($uri);
|
889 |
}
|
@@ -899,18 +916,61 @@ class Helpers
|
|
899 |
$headers = $http_response_header;
|
900 |
}
|
901 |
|
902 |
-
} elseif (function_exists('curl_exec')) {
|
903 |
$curl = curl_init($uri);
|
904 |
|
905 |
-
//TODO: use $context to define additional curl options
|
906 |
-
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
|
907 |
-
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
|
908 |
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
909 |
curl_setopt($curl, CURLOPT_HEADER, true);
|
910 |
if ($offset > 0) {
|
911 |
curl_setopt($curl, CURLOPT_RESUME_FROM, $offset);
|
912 |
}
|
913 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
914 |
$data = curl_exec($curl);
|
915 |
|
916 |
if ($data !== false && !curl_errno($curl)) {
|
57 |
public static function build_url($protocol, $host, $base_path, $url)
|
58 |
{
|
59 |
$protocol = mb_strtolower($protocol);
|
60 |
+
if (empty($protocol)) {
|
61 |
+
$protocol = "file://";
|
62 |
+
}
|
63 |
if ($url === "") {
|
64 |
+
return null;
|
|
|
65 |
}
|
66 |
|
67 |
+
$url_lc = mb_strtolower($url);
|
68 |
+
|
69 |
// Is the url already fully qualified, a Data URI, or a reference to a named anchor?
|
70 |
// File-protocol URLs may require additional processing (e.g. for URLs with a relative path)
|
71 |
+
if (
|
72 |
+
(
|
73 |
+
mb_strpos($url_lc, "://") !== false
|
74 |
+
&& !in_array(substr($url_lc, 0, 7), ["file://", "phar://"], true)
|
75 |
+
)
|
76 |
+
|| mb_substr($url_lc, 0, 1) === "#"
|
77 |
+
|| mb_strpos($url_lc, "data:") === 0
|
78 |
+
|| mb_strpos($url_lc, "mailto:") === 0
|
79 |
+
|| mb_strpos($url_lc, "tel:") === 0
|
80 |
+
) {
|
81 |
return $url;
|
82 |
}
|
83 |
|
84 |
+
$res = "";
|
85 |
+
if (strpos($url_lc, "file://") === 0) {
|
86 |
$url = substr($url, 7);
|
87 |
+
$protocol = "file://";
|
88 |
+
} elseif (strpos($url_lc, "phar://") === 0) {
|
89 |
+
$res = substr($url, strpos($url_lc, ".phar")+5);
|
90 |
+
$url = substr($url, 7, strpos($url_lc, ".phar")-2);
|
91 |
+
$protocol = "phar://";
|
92 |
}
|
93 |
|
94 |
$ret = "";
|
|
|
|
|
|
|
95 |
|
96 |
+
$is_local_path = in_array($protocol, ["file://", "phar://"], true);
|
97 |
+
|
98 |
+
if ($is_local_path) {
|
99 |
//On Windows local file, an abs path can begin also with a '\' or a drive letter and colon
|
100 |
//drive: followed by a relative path would be a drive specific default folder.
|
101 |
//not known in php app code, treat as abs path
|
106 |
}
|
107 |
$ret .= $url;
|
108 |
$ret = preg_replace('/\?(.*)$/', "", $ret);
|
109 |
+
|
110 |
+
$filepath = realpath($ret);
|
111 |
+
if ($filepath === false) {
|
112 |
+
return null;
|
113 |
+
}
|
114 |
+
|
115 |
+
$ret = "$protocol$filepath$res";
|
116 |
+
|
117 |
return $ret;
|
118 |
}
|
119 |
|
120 |
+
$ret = $protocol;
|
121 |
// Protocol relative urls (e.g. "//example.org/style.css")
|
122 |
if (strpos($url, '//') === 0) {
|
123 |
$ret .= substr($url, 2);
|
457 |
$host = "";
|
458 |
$path = "";
|
459 |
$file = "";
|
460 |
+
$res = "";
|
461 |
|
462 |
$arr = parse_url($url);
|
463 |
if ( isset($arr["scheme"]) ) {
|
464 |
$arr["scheme"] = mb_strtolower($arr["scheme"]);
|
465 |
}
|
466 |
|
467 |
+
if (isset($arr["scheme"]) && $arr["scheme"] !== "file" && $arr["scheme"] !== "phar" && strlen($arr["scheme"]) > 1) {
|
|
|
468 |
$protocol = $arr["scheme"] . "://";
|
469 |
|
470 |
if (isset($arr["user"])) {
|
506 |
|
507 |
} else {
|
508 |
|
509 |
+
$protocol = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
510 |
$host = ""; // localhost, really
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
511 |
|
512 |
+
$i = mb_stripos($url, "://");
|
513 |
+
if ($i !== false) {
|
514 |
+
$protocol = mb_strtolower(mb_substr($url, 0, $i + 3));
|
515 |
+
$url = mb_substr($url, $i + 3);
|
516 |
} else {
|
517 |
+
$protocol = "file://";
|
518 |
+
}
|
|
|
|
|
519 |
|
520 |
+
if ($protocol === "phar://") {
|
521 |
+
$res = substr($url, stripos($url, ".phar")+5);
|
522 |
+
$url = substr($url, 7, stripos($url, ".phar")-2);
|
|
|
|
|
523 |
}
|
524 |
+
|
525 |
+
$file = basename($url);
|
526 |
+
$path = dirname($url) . "/";
|
527 |
}
|
528 |
|
529 |
$ret = [$protocol, $host, $path, $file,
|
530 |
"protocol" => $protocol,
|
531 |
"host" => $host,
|
532 |
"path" => $path,
|
533 |
+
"file" => $file,
|
534 |
+
"resource" => $res];
|
535 |
return $ret;
|
536 |
}
|
537 |
|
592 |
{
|
593 |
if ($c <= 0x7F) {
|
594 |
return chr($c);
|
595 |
+
} elseif ($c <= 0x7FF) {
|
596 |
return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
|
597 |
+
} elseif ($c <= 0xFFFF) {
|
598 |
return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
|
599 |
. chr(0x80 | $c & 0x3F);
|
600 |
+
} elseif ($c <= 0x10FFFF) {
|
601 |
return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
|
602 |
. chr(0x80 | $c >> 6 & 0x3F)
|
603 |
. chr(0x80 | $c & 0x3F);
|
884 |
* - curl: if allow_url_fopen is false and curl is available
|
885 |
*
|
886 |
* @param string $uri
|
887 |
+
* @param resource $context
|
888 |
* @param int $offset
|
889 |
+
* @param int $maxlen
|
890 |
* @return string[]
|
891 |
*/
|
892 |
public static function getFileContent($uri, $context = null, $offset = 0, $maxlen = null)
|
894 |
$content = null;
|
895 |
$headers = null;
|
896 |
[$protocol] = Helpers::explode_url($uri);
|
897 |
+
$is_local_path = in_array(strtolower($protocol), ["", "file://", "phar://"], true);
|
898 |
+
$can_use_curl = in_array(strtolower($protocol), ["http://", "https://"], true);
|
899 |
|
900 |
set_error_handler([self::class, 'record_warnings']);
|
901 |
|
902 |
try {
|
903 |
+
if ($is_local_path || ini_get('allow_url_fopen') || !$can_use_curl) {
|
904 |
if ($is_local_path === false) {
|
905 |
$uri = Helpers::encodeURI($uri);
|
906 |
}
|
916 |
$headers = $http_response_header;
|
917 |
}
|
918 |
|
919 |
+
} elseif ($can_use_curl && function_exists('curl_exec')) {
|
920 |
$curl = curl_init($uri);
|
921 |
|
|
|
|
|
|
|
922 |
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
923 |
curl_setopt($curl, CURLOPT_HEADER, true);
|
924 |
if ($offset > 0) {
|
925 |
curl_setopt($curl, CURLOPT_RESUME_FROM, $offset);
|
926 |
}
|
927 |
|
928 |
+
if ($maxlen > 0) {
|
929 |
+
curl_setopt($curl, CURLOPT_BUFFERSIZE, 128);
|
930 |
+
curl_setopt($curl, CURLOPT_NOPROGRESS, false);
|
931 |
+
curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, function ($res, $download_size_total, $download_size, $upload_size_total, $upload_size) use ($maxlen) {
|
932 |
+
return ($download_size > $maxlen) ? 1 : 0;
|
933 |
+
});
|
934 |
+
}
|
935 |
+
|
936 |
+
$context_options = [];
|
937 |
+
if (!is_null($context)) {
|
938 |
+
$context_options = stream_context_get_options($context);
|
939 |
+
}
|
940 |
+
foreach ($context_options as $stream => $options) {
|
941 |
+
foreach ($options as $option => $value) {
|
942 |
+
$key = strtolower($stream) . ":" . strtolower($option);
|
943 |
+
switch ($key) {
|
944 |
+
case "curl:curl_verify_ssl_host":
|
945 |
+
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, !$value ? 0 : 2);
|
946 |
+
break;
|
947 |
+
case "curl:max_redirects":
|
948 |
+
curl_setopt($curl, CURLOPT_MAXREDIRS, $value);
|
949 |
+
break;
|
950 |
+
case "http:follow_location":
|
951 |
+
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $value);
|
952 |
+
break;
|
953 |
+
case "http:header":
|
954 |
+
if (is_string($value)) {
|
955 |
+
curl_setopt($curl, CURLOPT_HTTPHEADER, [$value]);
|
956 |
+
} else {
|
957 |
+
curl_setopt($curl, CURLOPT_HTTPHEADER, $value);
|
958 |
+
}
|
959 |
+
break;
|
960 |
+
case "http:timeout":
|
961 |
+
curl_setopt($curl, CURLOPT_TIMEOUT, $value);
|
962 |
+
break;
|
963 |
+
case "http:user_agent":
|
964 |
+
curl_setopt($curl, CURLOPT_USERAGENT, $value);
|
965 |
+
break;
|
966 |
+
case "curl:curl_verify_ssl_peer":
|
967 |
+
case "ssl:verify_peer":
|
968 |
+
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $value);
|
969 |
+
break;
|
970 |
+
}
|
971 |
+
}
|
972 |
+
}
|
973 |
+
|
974 |
$data = curl_exec($curl);
|
975 |
|
976 |
if ($data !== false && !curl_errno($curl)) {
|
vendor/dompdf/dompdf/src/Image/Cache.php
CHANGED
@@ -9,7 +9,7 @@
|
|
9 |
*/
|
10 |
namespace Dompdf\Image;
|
11 |
|
12 |
-
use Dompdf\
|
13 |
use Dompdf\Helpers;
|
14 |
use Dompdf\Exception\ImageException;
|
15 |
|
@@ -43,13 +43,6 @@ class Cache
|
|
43 |
|
44 |
public static $error_message = "Image not found or type unknown";
|
45 |
|
46 |
-
/**
|
47 |
-
* Current dompdf instance
|
48 |
-
*
|
49 |
-
* @var Dompdf
|
50 |
-
*/
|
51 |
-
protected static $_dompdf;
|
52 |
-
|
53 |
/**
|
54 |
* Resolve and fetch an image for use.
|
55 |
*
|
@@ -57,130 +50,124 @@ class Cache
|
|
57 |
* @param string $protocol Default protocol if none specified in $url
|
58 |
* @param string $host Default host if none specified in $url
|
59 |
* @param string $base_path Default path if none specified in $url
|
60 |
-
* @param
|
61 |
*
|
62 |
-
* @
|
63 |
-
*
|
64 |
*/
|
65 |
-
static function resolve_url($url, $protocol, $host, $base_path,
|
66 |
{
|
67 |
-
|
68 |
-
|
69 |
-
$
|
70 |
-
$parsed_url = Helpers::explode_url($url);
|
71 |
$message = null;
|
72 |
-
|
73 |
-
$remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] !== "");
|
74 |
-
|
75 |
-
$data_uri = strpos($parsed_url['protocol'], "data:") === 0;
|
76 |
-
$full_url = null;
|
77 |
-
$enable_remote = $dompdf->getOptions()->getIsRemoteEnabled();
|
78 |
-
$tempfile = false;
|
79 |
-
|
80 |
try {
|
|
|
81 |
|
82 |
-
|
83 |
-
|
84 |
-
throw new ImageException("Remote file access is disabled.", E_WARNING);
|
85 |
}
|
86 |
-
|
87 |
-
// remote allowed or DataURI
|
88 |
-
if (($enable_remote && $remote) || $data_uri) {
|
89 |
-
// Download remote files to a temporary directory
|
90 |
-
$full_url = Helpers::build_url($protocol, $host, $base_path, $url);
|
91 |
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
100 |
}
|
101 |
-
|
102 |
-
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
|
|
|
|
|
|
111 |
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
} // Image found, put in cache and process
|
117 |
-
else {
|
118 |
-
//e.g. fetch.php?media=url.jpg&cache=1
|
119 |
-
//- Image file name might be one of the dynamic parts of the url, don't strip off!
|
120 |
-
//- a remote url does not need to have a file extension at all
|
121 |
-
//- local cached file does not have a matching file extension
|
122 |
-
//Therefore get image type from the content
|
123 |
-
if (@file_put_contents($resolved_url, $image) === false) {
|
124 |
-
throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
|
125 |
-
}
|
126 |
}
|
|
|
|
|
127 |
}
|
128 |
-
} // Not remote, local image
|
129 |
-
else {
|
130 |
-
$resolved_url = Helpers::build_url($protocol, $host, $base_path, $url);
|
131 |
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
$chrootPath = realpath($chrootPath);
|
141 |
-
if ($chrootPath !== false && strpos($realfile, $chrootPath) === 0) {
|
142 |
-
$chrootValid = true;
|
143 |
-
break;
|
144 |
-
}
|
145 |
-
}
|
146 |
-
if ($chrootValid !== true) {
|
147 |
-
throw new ImageException("Permission denied on $resolved_url. The file could not be found under the paths specified by Options::chroot.", E_WARNING);
|
148 |
-
}
|
149 |
-
}
|
150 |
-
|
151 |
-
if (!$realfile) {
|
152 |
-
throw new ImageException("File '$realfile' not found.", E_WARNING);
|
153 |
-
}
|
154 |
-
|
155 |
-
$resolved_url = $realfile;
|
156 |
}
|
|
|
|
|
157 |
}
|
158 |
|
159 |
// Check if the local file is readable
|
160 |
if (!is_readable($resolved_url) || !filesize($resolved_url)) {
|
161 |
throw new ImageException("Image not readable or empty", E_WARNING);
|
162 |
-
}
|
163 |
-
|
164 |
-
|
|
|
|
|
|
|
|
|
165 |
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
}
|
173 |
-
|
174 |
-
else {
|
175 |
-
throw new ImageException("Image type unknown", E_WARNING);
|
176 |
}
|
|
|
177 |
}
|
178 |
} catch (ImageException $e) {
|
179 |
if ($tempfile) {
|
180 |
unlink($tempfile);
|
181 |
}
|
182 |
$resolved_url = self::$broken_image;
|
183 |
-
$type =
|
184 |
$message = self::$error_message;
|
185 |
Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine());
|
186 |
self::$_cache[$full_url] = $resolved_url;
|
@@ -229,7 +216,9 @@ class Cache
|
|
229 |
if ($debugPng) {
|
230 |
print "[clear unlink $file]";
|
231 |
}
|
232 |
-
|
|
|
|
|
233 |
}
|
234 |
|
235 |
foreach (self::$tempImages as $versions) {
|
9 |
*/
|
10 |
namespace Dompdf\Image;
|
11 |
|
12 |
+
use Dompdf\Options;
|
13 |
use Dompdf\Helpers;
|
14 |
use Dompdf\Exception\ImageException;
|
15 |
|
43 |
|
44 |
public static $error_message = "Image not found or type unknown";
|
45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
/**
|
47 |
* Resolve and fetch an image for use.
|
48 |
*
|
50 |
* @param string $protocol Default protocol if none specified in $url
|
51 |
* @param string $host Default host if none specified in $url
|
52 |
* @param string $base_path Default path if none specified in $url
|
53 |
+
* @param Options $options An instance of Dompdf\Options
|
54 |
*
|
55 |
+
* @return array An array with three elements: The local path to the image, the image
|
56 |
+
* extension, and an error message if the image could not be cached
|
57 |
*/
|
58 |
+
static function resolve_url($url, $protocol, $host, $base_path, Options $options)
|
59 |
{
|
60 |
+
$tempfile = null;
|
61 |
+
$resolved_url = null;
|
62 |
+
$type = null;
|
|
|
63 |
$message = null;
|
64 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
try {
|
66 |
+
$full_url = Helpers::build_url($protocol, $host, $base_path, $url);
|
67 |
|
68 |
+
if ($full_url === null) {
|
69 |
+
throw new ImageException("Unable to parse image URL $url.", E_WARNING);
|
|
|
70 |
}
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
+
$parsed_url = Helpers::explode_url($full_url);
|
73 |
+
$protocol = strtolower($parsed_url["protocol"]);
|
74 |
+
$is_data_uri = strpos($protocol, "data:") === 0;
|
75 |
+
|
76 |
+
if (!$is_data_uri) {
|
77 |
+
$allowed_protocols = $options->getAllowedProtocols();
|
78 |
+
if (!array_key_exists($protocol, $allowed_protocols)) {
|
79 |
+
throw new ImageException("Permission denied on $url. The communication protocol is not supported.", E_WARNING);
|
80 |
+
}
|
81 |
+
foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
|
82 |
+
[$result, $message] = $rule($full_url);
|
83 |
+
if (!$result) {
|
84 |
+
throw new ImageException("Error loading $url: $message", E_WARNING);
|
85 |
}
|
86 |
+
}
|
87 |
+
}
|
88 |
|
89 |
+
if ($protocol === "file://") {
|
90 |
+
$resolved_url = $full_url;
|
91 |
+
} elseif (isset(self::$_cache[$full_url])) {
|
92 |
+
$resolved_url = self::$_cache[$full_url];
|
93 |
+
} else {
|
94 |
+
$tmp_dir = $options->getTempDir();
|
95 |
+
if (($resolved_url = @tempnam($tmp_dir, "ca_dompdf_img_")) === false) {
|
96 |
+
throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
|
97 |
+
}
|
98 |
+
$tempfile = $resolved_url;
|
99 |
|
100 |
+
$image = null;
|
101 |
+
if ($is_data_uri) {
|
102 |
+
if (($parsed_data_uri = Helpers::parse_data_uri($url)) !== false) {
|
103 |
+
$image = $parsed_data_uri["data"];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
}
|
105 |
+
} else {
|
106 |
+
list($image, $http_response_header) = Helpers::getFileContent($full_url, $options->getHttpContext());
|
107 |
}
|
|
|
|
|
|
|
108 |
|
109 |
+
// Image not found or invalid
|
110 |
+
if ($image === null) {
|
111 |
+
$msg = ($is_data_uri ? "Data-URI could not be parsed" : "Image not found");
|
112 |
+
throw new ImageException($msg, E_WARNING);
|
113 |
+
}
|
114 |
+
|
115 |
+
if (@file_put_contents($resolved_url, $image) === false) {
|
116 |
+
throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
}
|
118 |
+
|
119 |
+
self::$_cache[$full_url] = $resolved_url;
|
120 |
}
|
121 |
|
122 |
// Check if the local file is readable
|
123 |
if (!is_readable($resolved_url) || !filesize($resolved_url)) {
|
124 |
throw new ImageException("Image not readable or empty", E_WARNING);
|
125 |
+
}
|
126 |
+
|
127 |
+
list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext());
|
128 |
+
|
129 |
+
if (($width && $height && in_array($type, ["gif", "png", "jpeg", "bmp", "svg","webp"], true)) === false) {
|
130 |
+
throw new ImageException("Image type unknown", E_WARNING);
|
131 |
+
}
|
132 |
|
133 |
+
if ($type === "svg") {
|
134 |
+
$parser = xml_parser_create("utf-8");
|
135 |
+
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
|
136 |
+
xml_set_element_handler(
|
137 |
+
$parser,
|
138 |
+
function ($parser, $name, $attributes) use ($options, $parsed_url, $full_url) {
|
139 |
+
if ($name === "image") {
|
140 |
+
$attributes = array_change_key_case($attributes, CASE_LOWER);
|
141 |
+
$url = $attributes["xlink:href"] ?? $attributes["href"];
|
142 |
+
if (!empty($url)) {
|
143 |
+
$inner_full_url = Helpers::build_url($parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $url);
|
144 |
+
if ($inner_full_url === $full_url) {
|
145 |
+
throw new ImageException("SVG self-reference is not allowed", E_WARNING);
|
146 |
+
}
|
147 |
+
[$resolved_url, $type, $message] = self::resolve_url($url, $parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $options);
|
148 |
+
if (!empty($message)) {
|
149 |
+
throw new ImageException("This SVG document references a restricted resource. $message", E_WARNING);
|
150 |
+
}
|
151 |
+
}
|
152 |
+
}
|
153 |
+
},
|
154 |
+
false
|
155 |
+
);
|
156 |
+
|
157 |
+
if (($fp = fopen($resolved_url, "r")) !== false) {
|
158 |
+
while ($line = fread($fp, 8192)) {
|
159 |
+
xml_parse($parser, $line, false);
|
160 |
}
|
161 |
+
fclose($fp);
|
|
|
|
|
162 |
}
|
163 |
+
xml_parser_free($parser);
|
164 |
}
|
165 |
} catch (ImageException $e) {
|
166 |
if ($tempfile) {
|
167 |
unlink($tempfile);
|
168 |
}
|
169 |
$resolved_url = self::$broken_image;
|
170 |
+
list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext());
|
171 |
$message = self::$error_message;
|
172 |
Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine());
|
173 |
self::$_cache[$full_url] = $resolved_url;
|
216 |
if ($debugPng) {
|
217 |
print "[clear unlink $file]";
|
218 |
}
|
219 |
+
if (file_exists($file)) {
|
220 |
+
unlink($file);
|
221 |
+
}
|
222 |
}
|
223 |
|
224 |
foreach (self::$tempImages as $versions) {
|
vendor/dompdf/dompdf/src/LineBox.php
CHANGED
@@ -266,7 +266,7 @@ class LineBox
|
|
266 |
/**
|
267 |
* @param AbstractFrameDecorator $frame
|
268 |
*/
|
269 |
-
public function add_frame(Frame $frame)
|
270 |
{
|
271 |
$this->_frames[] = $frame;
|
272 |
|
@@ -377,7 +377,7 @@ class LineBox
|
|
377 |
*
|
378 |
* @return float
|
379 |
*/
|
380 |
-
public function recalculate_width()
|
381 |
{
|
382 |
$width = 0.0;
|
383 |
|
@@ -391,7 +391,7 @@ class LineBox
|
|
391 |
/**
|
392 |
* @return string
|
393 |
*/
|
394 |
-
public function __toString()
|
395 |
{
|
396 |
$props = ["wc", "y", "w", "h", "left", "right", "br"];
|
397 |
$s = "";
|
266 |
/**
|
267 |
* @param AbstractFrameDecorator $frame
|
268 |
*/
|
269 |
+
public function add_frame(Frame $frame): void
|
270 |
{
|
271 |
$this->_frames[] = $frame;
|
272 |
|
377 |
*
|
378 |
* @return float
|
379 |
*/
|
380 |
+
public function recalculate_width(): float
|
381 |
{
|
382 |
$width = 0.0;
|
383 |
|
391 |
/**
|
392 |
* @return string
|
393 |
*/
|
394 |
+
public function __toString(): string
|
395 |
{
|
396 |
$props = ["wc", "y", "w", "h", "left", "right", "br"];
|
397 |
$s = "";
|
vendor/dompdf/dompdf/src/Options.php
CHANGED
@@ -13,7 +13,7 @@ class Options
|
|
13 |
/**
|
14 |
* The location of a temporary directory.
|
15 |
*
|
16 |
-
* The directory specified must be writable by the
|
17 |
* The temporary directory is required to download remote images and when
|
18 |
* using the PFDLib back end.
|
19 |
*
|
@@ -25,7 +25,7 @@ class Options
|
|
25 |
* The location of the DOMPDF font directory
|
26 |
*
|
27 |
* The location of the directory where DOMPDF will store fonts and font metrics
|
28 |
-
* Note: This directory must exist and be writable by the
|
29 |
*
|
30 |
* @var string
|
31 |
*/
|
@@ -37,7 +37,7 @@ class Options
|
|
37 |
* This directory contains the cached font metrics for the fonts used by DOMPDF.
|
38 |
* This directory can be the same as $fontDir
|
39 |
*
|
40 |
-
* Note: This directory must exist and be writable by the
|
41 |
*
|
42 |
* @var string
|
43 |
*/
|
@@ -46,10 +46,10 @@ class Options
|
|
46 |
/**
|
47 |
* dompdf's "chroot"
|
48 |
*
|
49 |
-
*
|
50 |
-
* All local files opened by dompdf must be in a subdirectory of
|
51 |
-
* or
|
52 |
-
* DO NOT set
|
53 |
* read any files on the server. This should be an absolute path.
|
54 |
*
|
55 |
* ==== IMPORTANT ====
|
@@ -62,20 +62,31 @@ class Options
|
|
62 |
*/
|
63 |
private $chroot;
|
64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
/**
|
66 |
* @var string
|
67 |
|
13 |
/**
|
14 |
* The location of a temporary directory.
|
15 |
*
|
16 |
+
* The directory specified must be writable by the executing process.
|
17 |
* The temporary directory is required to download remote images and when
|
18 |
* using the PFDLib back end.
|
19 |
*
|
25 |
* The location of the DOMPDF font directory
|
26 |
*
|
27 |
* The location of the directory where DOMPDF will store fonts and font metrics
|
28 |
+
* Note: This directory must exist and be writable by the executing process.
|
29 |
*
|
30 |
* @var string
|
31 |
*/
|
37 |
* This directory contains the cached font metrics for the fonts used by DOMPDF.
|
38 |
* This directory can be the same as $fontDir
|
39 |
*
|
40 |
+
* Note: This directory must exist and be writable by the executing process.
|
41 |
*
|
42 |
* @var string
|
43 |
*/
|
46 |
/**
|
47 |
* dompdf's "chroot"
|
48 |
*
|
49 |
+
* Utilized by Dompdf's default file:// protocol URI validation rule.
|
50 |
+
* All local files opened by dompdf must be in a subdirectory of the directory
|
51 |
+
* or directories specified by this option.
|
52 |
+
* DO NOT set this value to '/' since this could allow an attacker to use dompdf to
|
53 |
* read any files on the server. This should be an absolute path.
|
54 |
*
|
55 |
* ==== IMPORTANT ====
|
62 |
*/
|
63 |
private $chroot;
|
64 |
|
65 |
+
/**
|
66 |
+
* Protocol whitelist
|
67 |
+
*
|
68 |
+
* Protocols and PHP wrappers allowed in URIs, and the validation rules
|
69 |
+
* that determine if a resouce may be loaded. Full support is not guaranteed
|
70 |
+
* for the protocols/wrappers specified
|
71 |
+
* by this array.
|
72 |
+
*
|
73 |
+
* @var array
|
74 |
+
*/
|
75 |
+
private $allowedProtocols = [
|
76 |
+
"file://" => ["rules" => []],
|
77 |
+
"http://" => ["rules" => []],
|
78 |
+
"https://" => ["rules" => []]
|
79 |
+
];
|
80 |
+
|
81 |
/**
|
82 |
* @var string
|
83 |
|